GPIO in the kernel: future directions
No more numbers
The current GPIO API relies on simple integers to identify specific GPIO lines. It works, but there are some shortcomings to this approach. Kernel code is rarely interested in "GPIO #37"; instead, it wants "the GPIO connected to the monitor's DDC line" or something to that effect. For well-defined systems where the use of GPIO lines never changes, preprocessor definitions can be used to identify lines, but that approach falls apart when the same GPIO can be put to different uses in different systems. As hardware gets more dynamic, with GPIOs possibly showing up at any time, there is no easy way to know which GPIO goes where. It can be easy to get the wrong one by mistake.
As a result, platform and driver developers have come up with various ways to locate GPIOs of interest. Even your editor once submitted a patch adding a gpio_lookup() function to the GPIO API, but that patch didn't pass muster and was eventually dropped in favor of a driver-specific solution. So the number-based API has remained — until now.
Alexandre Courbot's descriptor-based GPIO interface seeks to change the situation by introducing a new struct gpio_desc * pointer type. GPIO lines would be represented by one of these pointers; what lives behind the pointer would be hidden from GPIO users, though. Internally, gpiolib (the implementation of the GPIO API used by most architectures) is refactored to use descriptors rather than numbers, and a new set of functions is presented to users. These functions will look familiar to users of the current GPIO API:
#include <linux/gpio/consumer.h> int gpiod_direction_input(struct gpio_desc *desc); int gpiod_direction_output(struct gpio_desc *desc, int value); int gpiod_get_value(struct gpio_desc *desc); void gpiod_set_value(struct gpio_desc *desc, int value); int gpiod_to_irq(struct gpio_desc *desc); int gpiod_export(struct gpio_desc *desc, bool direction_may_change); int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc); void gpiod_unexport(struct gpio_desc *desc);
In short: the gpio_ prefix on the existing GPIO functions has been changed to gpiod_ and the integer GPIO number argument is now a struct gpio_desc *. There is also a new include file for the new functions; otherwise the interfaces are identical. The existing, integer-based API still exists, but it has been reimplemented as a layer on top of the descriptor-based API shown here.
What is missing from the above list, though, is any way of obtaining a descriptor for a GPIO line in the first place. One way to do that is to get the descriptor from the traditional GPIO number:
struct gpio_desc *gpio_to_desc(unsigned gpio);
There is also a desc_to_gpio() for going in the opposite direction. Using this function makes it easy to transition existing code over to the new API. Obtaining a descriptor in this manner will ensure that no code accesses a GPIO without having first properly obtained a descriptor for it, but it would be better to do away with the numbers altogether in favor of a more robust way of looking up GPIOs. The patch set adds this functionality in this form:
struct gpio_desc *gpiod_get(struct device *dev, const char *name);
Here, dev should be the device providing the GPIO line, and "name" describes that line. The dev pointer is needed to disambiguate the name, and because code accessing a GPIO line should know which device it is working through in any case. So, for example, a video acquisition bridge device may need access to GPIO lines with names like "sensor-power", "sensor-reset", "sensor-i2c-clock" and "sensor-i2c-data". The driver could then request those lines by name with gpiod_get() without ever having to be concerned with numbers.
Needless to say, there is a gpiod_put() for releasing access to a GPIO line.
The actual association of names with GPIO lines can be done by the driver that implements those lines, if the names are static and known. In many cases, though, the routing of GPIO lines will have been done by whoever designed a specific system-on-chip or board; there is no way for the driver author to know ahead of time how a specific system may be wired. In this case, the names of the GPIO lines will most likely be specified in the device tree, or, if all else fails, in a platform data structure.
The response to this interface is generally positive; it seems almost certain that it will be merged in the near future. The biggest remaining concern, perhaps, is that the descriptor interface is implemented entirely within the gpiolib layer. Most architectures use gpiolib to implement the GPIO interface, but it is not mandatory; in some cases, the gpio_* functions are implemented as macros that access the device registers directly. Such an implementation is probably more efficient, but GPIO is not usually a performance-critical part of the system. So there may be pressure for all architectures to move to gpiolib; that, in turn, would facilitate the eventual removal of the number-based API entirely.
Block GPIO
The GPIO interface as described so far is focused on the management of individual GPIO lines. But GPIOs are often used together as a group. As a simple example, consider a pair of GPIOs used as an I2C bus; one line handles data, the other the clock. A bit-banging driver can manage those two lines together to communicate with connected I2C devices; the kernel contains a driver in drivers/i2c/busses/i2-gpio.c for just this purpose.
Most of the time, managing GPIOs individually, even when they are used as a group, works fine. Computers are quite fast relative to the timing requirements of most of the serial communications protocols that are subject to implementation with GPIO. But there are exceptions, especially when the hardware implementing the GPIO lines themselves is slow; that can make it hard to change multiple lines in a simultaneous manner. But, sometimes, the hardware can change lines simultaneously if properly asked; often the lines are represented by bits in the same device register and can all be changed together with a single I/O memory write operation.
Roland Stigge's block GPIO patch set is an attempt to make that functionality available in the kernel. Code that needs to manipulate multiple GPIOs as a group would start by associating them in a single block with:
struct gpio_block *gpio_block_create(unsigned int *gpios, size_t size, const char *name);
gpios points to an array of size GPIO numbers which are to be grouped into a block; the given name can be used to work with the block from user space. The GPIOs should have already been requested with gpio_request(); they also need to have their direction set individually. It's worth noting that the GPIOs need not be located on the same hardware; if they are spread out, or if the underlying driver does not implement the internal block API, the block GPIO interface will just access those lines individually as is done now.
Manipulation of GPIO blocks is done with:
unsigned long gpio_block_get(struct gpio_block *block, unsigned long mask); void gpio_block_set(struct gpio_block *block, unsigned long mask, unsigned long values);
For both functions, block is a GPIO block created as described above, and mask is a bitmask specifying which GPIOs in the block are to be acted upon; each bit in mask enables the corresponding GPIO in the array passed to gpio_block_create(). This API implies that the number of bits in a long forces an upper bound on number of lines grouped into a GPIO block; that seems unlikely to be a problem in real-world use. gpio_block_get() will read the specified lines, simultaneously if possible, and return a bitmask with the result. The lines in a GPIO block can be set as a unit with gpio_block_set().
A GPIO block is released with:
void gpio_block_free(struct gpio_block *block);
There is also a pair of registration functions:
int gpio_block_register(struct gpio_block *block); void gpio_block_unregister(struct gpio_block *block);
Registering a GPIO block makes it available to user space. There is a sysfs interface that can be used to query and set the GPIOs in a block. Interestingly, registration also creates a device node (using the name provided to gpio_block_create()); reading from that device returns the current state of the GPIOs in the block, while writing it will set the GPIOs accordingly. There is an ioctl() operation (which, strangely, uses zero as the command number) to set the mask to be used with read and write operations.
This patch set has not generated as much discussion as the descriptor-based
API patches (it is also obviously not yet integrated with the descriptor
API). Most likely, relatively few developers have felt the need for a
block-based API. That said, there are cases when it is likely to be
useful, and there appears to be no opposition, so this API can eventually
be expected to be merged as well.
Index entries for this article | |
---|---|
Kernel | General-purpose I/O |
Kernel | GPIO |
Posted Jan 24, 2013 3:06 UTC (Thu)
by dlang (guest, #313)
[Link] (2 responses)
It also means that the reliability degrades significantly as the load goes up. This will mean that it may never fail when the developer is testing it, but in teh real-world with higher load (and much longer operational periods) failures are almost guaranteed to take place.
If you try to group together GPIO lines that are on different hardware, you aren't going to be perfectly reliable, but in most cases, the GPIO lines really are grouped on the hardware level, non-uncommonly, to the point where some layer is reading the state of a bank of lines, modifying it based on what's requested, and writing the new state out to all the lines at once.
P.S. has anyone made a shim layer to allow you to use a parallel port as a set of GPIO lines?
Posted Jan 25, 2013 18:29 UTC (Fri)
by zlynx (guest, #2285)
[Link] (1 responses)
The program runs SCHED_OTHER as normal, then escalates into SCHED_FIFO at prio 95 (something like that) to program the data ports, then falls back.
I know that kernel interrupts and similar things may interfere but it was never a problem in practice. The LCD's timing requirements were not overly strict, just that you couldn't go away and ignore it for tens of milliseconds.
Posted Jan 25, 2013 19:59 UTC (Fri)
by dlang (guest, #313)
[Link]
Posted Jan 24, 2013 14:02 UTC (Thu)
by linusw (subscriber, #40300)
[Link] (2 responses)
I have (as GPIO co-maintainer) raised my concerns about the new grouped GPIO sysfs API, and left it for Grant to decide upon. I can live with it, but I'm not overly happy with this ABI/API having to be perpetually supported.
My main concern is about use cases and I really haven't seen anybody step up and say: "I use this for X, Y, and Z". If that X, Y and Z is a fan on a laptop, a LED and some CD tray opener then yeah OK. But that does not seem to be the case, as the entire basis for its existence is that it be time-critical.
For example, and related to the concerns that dlang raise above: what if these GPIO lines are controlling the servo motor of some robot, a dialysis machine or a fighter jet?
I have hear RUMORS about this kind of use cases. That people will stick a process in userspace which is basically dealing with automatic control somekindof. And it's time critical, so the system is basically just running this one process, and it is elevated to real-time priority, so starvation etc should not occur.
If you have the new SCHED_DEADLINE you could maybe do things like this (obviously this is done from userspace):
I don't know if this kind of things are what we're designing for with our sysfs interface, and I feel slightly inconvenient not knowing. Please use the comment field here to tell us all about your plans for userspace GPIO.
Posted Jan 24, 2013 18:12 UTC (Thu)
by jimparis (guest, #38647)
[Link] (1 responses)
There are lots of cases where you want things to happen simultaneously, but they're not time critical. For example, let's say you drive an RGB LED on a cell phone with three GPIOs. If it's currently yellow (R+G) and you want to change it to blue (B), you want to turn off two GPIOs and set one GPIO as a single operation. It's okay if the entire action is delayed by 250ms, but it's not okay to change one bit, then have a delay, then change the other two bits, because then you'll have a visibly long period with an unexpected third color.
Posted Jan 24, 2013 19:50 UTC (Thu)
by dlang (guest, #313)
[Link]
There is also the chance that changing multiple bits at once is much easier than changing one bit at a time.
If you were to use a parallel printer port as a GPIO (it has 8 outputs and 3 inputs IIRC), it's much easier to change all 8 bits at once than to change one bit at a time.
The only thing I find questionable with the concept is that it allows grouping GPIO lines that do not have any underlying connection, but that doesn't make things any worse than they are now (other than possibly fooling the programmer into thinking they are better than they are now), and the improvements in the code of things using GPIO lines can be significant.
one place this would make a huge amount of sense from a software point of view is if you are connected to a small LCD display through a parallel interface (very common for 16 character x 2 line displays for example).
with this interface, you can do
nibble = *c & 0x0F;
it would be much more work if you had to set each line independently.
Posted Jan 25, 2013 10:44 UTC (Fri)
by linusw (subscriber, #40300)
[Link] (1 responses)
One issue raised by Alan Cox concerns security, see this post:
How are people feeling about this? I for one worry that we implement security problems that will be hell to fix further down the road.
Posted Nov 14, 2013 13:40 UTC (Thu)
by bokr (subscriber, #58369)
[Link]
[BTW: the server behind the marc.info link seems to serve a page with missing <html> and <body> tags at the front]
when things are exposed to userspace, the bock based api is needed more
when things are exposed to userspace, the bock based api is needed more
when things are exposed to userspace, the bock based api is needed more
GPIO in the kernel: future directions
http://www.youtube.com/watch?v=UJSWvC-QnjI
GPIO in the kernel: future directions
GPIO in the kernel: future directions
output(nibble);
toggle(clock);
nibble = *c++ <<4 & 0x0F;
output(nibble);
toggle(clock);
GPIO in the kernel: future directions
http://marc.info/?l=linux-kernel&m=135489945103388&...
GPIO in the kernel: future directions