Note on the new linux kernel interface - gpio uapi

Starting with kernel version 4.6-r1, a new interface for interacting with the gpio kernel subsystem has become available to us. Now there are three official ways to work with gpio and get interrupts from them. There is no point in delving into the needs for this subsystem, for a small part it is a harsh everyday life, for the other part it’s a fun hobby, and for all together a new opportunity for interaction was provided in the kernel.


The note is popular in nature, since we will not touch on the main advantages that came with the innovation, namely the simplification of working with gpio in the context of the kernel.


New uapi gpio interface


https://github.com/torvalds/linux/blob/master/include/uapi/linux/gpio.h


Firstly, now gpiochip is really a device and it can be seen in devfs as gpiochipN , where N is the chip number assigned in the initialization order . Secondly, all configuration is now done through ioctl . And thirdly, reading and writing, surprisingly, are done through read / write calls, though with the help of a special struct gpiohandle_data structure .


gpio-mockup


Starting with the kernel version v4.9-rc1 (it will actually be possible to use it only in versions older than v4.12-rc1), a virtual gpio device has become available , with support for state management via debugfs .


For example, consider the difference between sysfs and uapi in userspace.


Initializing gpio-mockup


Parameters:


  • gpio_mockup_ranges - pairs of numbers to initialize gpiochips, in the form of "base, end", where base is the start number, end is the end of the range.
  • gpio_mockup_named_lines - boolean parameter, if set, assigns a label to each line in the form gpio-mockup-A..ZN, where N is the serial number of the line in the bank.

# modprobe gpio-mockup gpio_mockup_ranges=0,8,8,16 

With gpiochip with 8 lines each, with ranges [0-8), [8,16). We'll talk bit later.


Comparing sysfs and uapi


Using the new driver, we consider the differences between the two systems from the user's point of view.


Gpiochip's information


sysfs


    # cat /sys/class/gpio/gpiochip*/base
    0   
    8 
    # cat /sys/class/gpio/gpiochip*/ngpio
    8 
    8 
    # cat /sys/class/gpio/gpiochip*/label
    gpio-mockup-A 
    gpio-mockup-B 

uapi


    struct gpiochip_info chip_info; 
    ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip_info); 

    # ./lsgpio | grep GPIO\ chip 
    GPIO chip: gpiochip1, "gpio-mockup-B", 8 GPIO lines   
    GPIO chip: gpiochip0, "gpio-mockup-A", 8 GPIO lines 

Set line as input and read value


sysfs


    # echo in > /sys/class/gpio/gpio0/direction 
    # cat /sys/class/gpio/gpio0/value 

uapi


    struct gpiohandle_request req; 
    req.lineoffsets[0] = 0; 
    req.flags = GPIOHANDLE_REQUEST_INPUT; 
    req.lines = 1; 

    struct gpiohandle_data data; 

    ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); 
    ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); 

Setting the line as an output


sysfs


    # echo high > /sys/class/gpio/gpio0/direction 

uapi


    struct gpiohandle_request req; 
    req.lineoffsets[0] = 0; 
    req.flags = GPIOHANDLE_REQUEST_OUTPUT; 
    req.default_values[0] = 1; 
    req.lines = 1; 

    ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); 

Edge handling


sysfs


    # echo both > /sys/class/gpio/gpio0/edge 

uapi


    struct gpioevent_request ereq; 
    ereq.lineoffset = 0; 
    ereq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; 

    ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &ereq); 

Polling on events


In the kernel documentation for sysfs, it is indicated to use EPOLLPRI and EPOLLERR (or exceptfds for select), which, in principle, is typical for any call to sysfs_notify not necessarily for the gpio subsystem .


For uapi enough EPOLLIN .


    struct epoll_event event;
    event.data.fd = ereq.fd;
    event.events = EPOLLIN;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, ereq.fd, &event);

We read an event with a timestamp and type GPIOEVENT_EVENT_RISING_EDGE or GPIOEVENT_EVENT_FALLING_EDGE .


    struct gpioevent_data event; 
    read(pin->fd, &event, sizeof(event)); 

EPOLLET for uapi works according to the documentation on epoll.


labels


Little remark for sysfs


The contact name gpioN , which everyone is used to, is generally not canonical, but is used if the contact has not been given a name, for example, in Device Tree.


    // drivers/gpio/gpiolib-sysfs.c 
    // int gpiod_export(struct gpio_desc *desc, bool direction_may_change) 
    offset = gpio_chip_hwgpio(desc); 
    if (chip->names && chip->names[offset]) 
        ioname = chip->names[offset]; 
    dev = device_create_with_groups(&gpio_class, &gdev->dev, 
                                    MKDEV(0, 0), data, gpio_groups, 
                                    ioname ? ioname : "gpio%u", 
                                    desc_to_gpio(desc)); 

Let's try gpio-mockup with the gpio_mockup_named_lines option :


    # modprobe gpio-mockup gpio_mockup_ranges=0,8,8,16 gpio_mockup_named_lines=1 
    # echo 0 > /sys/class/gpio/export 
    # ls /sys/class/gpio/gpio-mockup-A-0   
    active_low device direction edge power  subsystem uevent value 

As we can see, the contact name has become gpio_chip_label - gpio_offset , but this is true only for the gpio-mockup driver .


    // drivers/gpio/gpio-mockup.c 
    // static int gpio_mockup_name_lines(struct device *dev, struct gpio_mockup_chip *chip) 
    for (i = 0; i < gc->ngpio; i++) 
        names[i] = devm_kasprintf(dev, GFP_KERNEL, "%s-%d", gc->label, i); 

It is not possible to uapi , and it is difficult to find the exported “named” line if the name is not known in advance (again, if it is known, then for the unique identification we need a name and an offset on the known gpiochip ).


uapi


The uapi interface allows us to see line names without initialization:


    #./lsgpio | grep gpio-mockup-A   
    GPIO chip: gpiochip0, "gpio-mockup-A", 8 GPIO lines
    line 0: "gpio-mockup-A-0" unused [output] 
    ... 
    line 7: "gpio-mockup-A-7" unused [output] 

With the corresponding device tree file (an example is taken from the kernel documentation):


    gpio-line-names = "MMC-CD", "MMC-WP", "VDD eth", "RST eth", "LED R", 
        "LED G", "LED B", "Col A", "Col B", "Col C", "Col D", 
        "Row A", "Row B", "Row C", "Row D", "NMI button", 
        "poweroff", "reset"; 

We would see a name in struct gpioline_info for each contact, unfortunately, few people name contacts, even for common SBC.


Uapi features not available for sysfs


Now we list the benefits not available to the old interface.


The main advantage I consider is the timestamp that is assigned to the event in the upper half of the interrupt handler. Which is indispensable for applications that need accuracy in measuring time between events.


    le->timestamp = ktime_get_real_ns(); 

If the device driver allows, the line can be additionally configured as an open collector ( GPIOLINE_FLAG_OPEN_DRAIN ) or an open emitter ( GPIOLINE_FLAG_OPEN_SOURCE ), this innovation can be easily transferred to sysfs , but this will not be possible since Linus Werley is against.


Also, the new api allows you to assign custom labels to each contact during initialization in the struct gpiohandle_request field consumer_label .


And in conclusion, it allows you to "read" and "write" immediately a group of states for contacts.


Conclusion Compared


Subjectively, uapi looks more cumbersome than sysfs , but do not forget that we compared control through standard GNU utilities cat and echo and C code, if we compare C code for each of the interfaces, it will turn out to be approximately the same in complexity and volume.


The important point is that in the case of using sysfs, the line remains initialized until the user asks for the opposite or until the reboot. uapi frees the line immediately after closing the file descriptor.


Uapi benefits


  • Saves us syscall (don't forget about the need for lseek after reading gpio / value).
  • Initializing an array of inputs or outputs.
  • Reading or writing an array of inputs or outputs.
  • Open drain and open source
  • Custom tags
  • "Real time nanosecond timestamp" passed in the event

Criticism uapi


There is no official or unofficial criticism, or I did not find it. Therefore, let’s get along with a couple of our own thoughts.


  • It is unclear why they bypassed push-pull, debounce, and pull-up, pull-down.
  • In struct gpioevent_data, it would be nice to add a value parameter with the current line value

Criticizing sysfs gpio


Let's end with official criticism of the sysfs interface. By Linus Vaerli (accompanying the gpio and pinctrl subsystems in the kernel), the following points were put forward in the patch comments:


  • It is impossible to turn off several lines at once with one call
  • For sysfs to work, the corresponding key must be enabled in the kernel configuration
  • In the event of a gpio application crash, the lines remain in an “initialized” state
  • Difficult to find the necessary line
  • "Sysfs is horribly broken" ©

In general, and to be honest, I think that sysfs is quite normal for those tasks that are assigned to gpio in userspace. It has something romantic in it, when people not even familiar with the basics of electrical engineering could turn on the light using echo . With the new interface, such a direct connection is not felt, since now additional utilities are required for interaction.


But GPIOs are often used together as a group. As a simple example (and the only), consider a pair of GPIOs used as an I2C bus; one line handles data, the other the clock.

I can’t say anything about the first thesis, I have never encountered such a need, in the end you can immediately initialize contacts as inputs or outputs in the device tree . I know that this functionality would be useful for those who need bit-banging in user-space , but here there is one thing, but on pure linux, bit-banging is possible only for very low-frequency things, and at least the PREEMPT_RT patch is needed .


The second thesis is also strange. I can not imagine such a saving in space that it would be necessary to disable sysfs.


The third is even stranger, maybe you just don’t need to “crash” the application?


As for the fourth, I can say that basically nothing has essentially changed. If the one specified in the platform driver or in the device tree label for gpiochip is true, then the "search" is simple, and if they are called "hell-like", then the interface will not help here.


In general, I could not find an intelligible answer. I am not against the new interface, I am even for it, but such a careful digging of the old interface is personally incomprehensible and unpleasant to me.


Utilities


For uapi, there are not so many of them as for sysfs.


Linux kernel gpio tools


https://github.com/torvalds/linux/tree/master/tools/gpio


  • gpio-event-mon - utility for tracking events of gpio lines
  • gpio-hammer - turn on / off the line n times with a fixed frequency
  • lsgpio - example listing of gpiochip and lines

libgpiod


https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/


From the author of the drivers gpio-mockup and irq-sim comrade Bartosz'a Gołaszewski.


Positioned by the author as a library, to facilitate the work with gpio through the new uapi interface, it also contains a set of useful utilities.


  • gpiodetect - listing of gpiochip with name, label and number of lines
  • gpioinfo - listing gpiochip named, offset label and line status
  • gpioget - read the state of the line
  • gpioset - set the line state, and if necessary, keep the line busy until the specified time, signal or user input expires
  • gpiofind - Finds a line by name and displays the gpiochipN
  • device and line offset
  • gpiomon is the same as gpio-event-mon

Materials


  1. GPIO Interfaces (legacy)
  2. Specifying GPIO information for devices
  3. A fresh look at the kernel's device model
  4. A fresh look at the kernel's device model, Linus W. comment
  5. simulated interrupts
  6. GPIO bulk changes for kernel v4.6

Videos


  1. GPIO for Engineers and Makers, Linus Walleij
  2. New GPIO Interface for User Space, Bartosz Golaszewski