Page MenuHomeFreeBSD

Provide userland notification of gpio pin changes (aka "userland gpio interrupts").
ClosedPublic

Authored by ian on Nov 27 2020, 5:50 PM.

Details

Summary

FreeBSD already has support for interrupts implemented in the GPIO controller drivers of several SoCs, but there are no interfaces to take advantage of them out of user space yet. The goal of this work is to implement such an interface by providing descriptors which integrate with the common I/O system calls and multiplexing mechanisms.

This is an import of the Google Summer of Code 2018 project completed by Christian Krämer (and, sadly, ignored by us for two years now).

The initial imported code supports the following functionality:

  • A kernel driver that provides an interface to the user space; the existing gpioc(4) driver was enhanced with this functionality.
  • Implement support for the most common I/O system calls / multiplexing mechanisms.
    • read() Places the pin number on which the interrupt occurred in the buffer. Blocking and non-blocking behaviour supported.
    • poll()/select()
    • kqueue()
    • signal driven I/O. Posting SIGIO when the O_ASYNC was set.
  • Many-to-many relationship between pins and file descriptors.
    • A file descriptor can monitor several GPIO pins.
    • A GPIO pin can be monitored by multiple file descriptors.
  • Integration with gpioctl and libgpio.
Test Plan

A test program exists at:

https://github.com/ckraemer/gsoc2018-utils/blob/master/src/gpioc_intr_test.c

A test program based on the above sources is now part of this change.

Diff Detail

Repository
rS FreeBSD src repository - subversion
Lint
Lint Skipped
Unit
Unit Tests Skipped
Build Status
Buildable 35082

Event Timeline

ian requested review of this revision.Nov 27 2020, 5:50 PM

Just a note to mention that the initial diff is the exact code from https://github.com/ckraemer/freebsd/tree/gsoc2018, with a couple small tweaks to resolve conflicts due to the freebsd base code having changed a bit over the past two years.

Locking fixes. The original code had several places where an error-handling path would exit a function with a mutex unlocked that would be re-locked on a normal exit. There was also a place where an SLIST was traversed without holding the mutex that protects additions and deletions on that list.

Primarily style fixes, mostly reformating block comments to the freebsd standard.

Also, several device_printf()s are now controlled by #ifdef GPIOC_DEBUG, so that there are normally no printfs done by the interrupt handler except when debugging is specifically enabled.

manu added inline comments.
sys/dev/gpio/gpioc.c
692

Something here should check the caps of the pin to check if interrupts are supported, otherwise we will configure them and report that it's done to the user when it will never work.
Allwinner don't have all pins with interrupts caps for example.

Allow only edge-triggered interrupts, and only if they are supported on that pin according to the capabilities flags from the hardware driver.

ian marked an inline comment as done.Nov 27 2020, 7:55 PM

I'm keen on having this - a build is under way. DHYB, it may take a while.

sys/dev/gpio/gpioc.c
451

Note that this check for last_intr_pin != 1 implies that userland must do a read(2) between each pin change event to avoid losing events. This is on a per-fd, not per-pin basis, so if a given fd is monitoring multiple pins and two of them change state simultaneously, one of the changes will get dropped. (I have work in progress to eliminate this problem.)

ian edited the test plan for this revision. (Show Details)

This update adds the option to choose between detailed (the default) and summary reporting, and removes the restriction that only one pin-change event can occur between each call to read(2) without losing data. It also adds a new test/demo program, based on the test code written by Christian for the gsoc project.

Detailed reporting reports each pin change event by reporting the pin number, timestamp of the event occurrance, and the pin state at the time of the event (useful when watching for both edges); a circular buffer is used to handle multiple events between read(2) calls. Summary reporting reports the pin number, the timestamp of the first event on that pin, the timestamp of the most recent event on that pin, and the number of pin-change events that occurred since the last read(2) call.

While working on the detailed vs summary reporting logic, I realized that the original gsoc code only allowed a single pin-change event to be recorded between each call to read(2), no matter how many pins were being monitored. If two pins changed simulataneously, you could only be notified about one of them; the other would be lost. These changes add a fifo (actually a circular buffer) to store multiple events between read() calls. By default the fifo size is 2*numpins where numpins is the number of pins supported by the hardware. In effect, this most usually results in a 64-event fifo on most modern hardware that organizes gpio pins into banks of 32 pins. Even on one-big-bank hardware such as Allwinner SoCs that have 150 pins, that's only 300 events (times 16 bytes per event, so not a lot of memory wasted).

A new ioctl is available to choose between detailed or summary reporting, and to optionally increase the size of the event fifo.

A few last minor tweaks to the test program.

This revision was not accepted when it landed; it landed in state Needs Review.Dec 12 2020, 6:34 PM
This revision was automatically updated to reflect the committed changes.