Implementation of a user space interface for GPIO interrupts
Student: Christian Krämer (uddka@student.kit.edu)
- Mentors:
Chuck Tuffli (chuck@FreeBSD.org)
Luiz Otavio O Souza (loos@FreeBSD.org)
Project description
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 project is to implement such an interface by providing descriptors which integrate with the common I/O system calls and multiplexing mechanisms.
Approach to solving the problem
Writing a kernel driver that sets the interrupts up and controls the interrupt related resources. Furthermore, the driver provides a handler function for the interrupts which serves the file descriptors. A user space API to create and configure those descriptors must also be implemented (ideally these functions would be integrated into libgpio and gpioctl). These API should allow the configuration of interrupt specific attributes like trigger-type. To conclude, some hardware tests shall be done on SoCs that support GPIO generated interrupts.
Upon suggestion of my mentors the major part of the kernel functionality was integrated in the already existing gpioc driver. This allows to use the already existing functions for interrupt configuration of libgpio to set and retrieve the configuration flags. The gpioctl utility was updated in order to be able to print the interrupt capabilities and flags.
Deliverables
Writing a kernel driver that provides an interface to the user space.
- 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.
poll()/select()
kqueue()
asynchronous I/O
- Since FreeBSD 11 asynchronous I/O operations are not allowed on all file descriptor types any more, because they are considered as unsafe. This behaviour also includes character device file descriptors.
signal driven I/O.
- Posting SIGIO when the O_ASYNC was set.
Pin distinction either by using separate character devices or file descriptors.
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.
- Interrupt rate limiting.
Test Plan
Hardware tests should be done on the BeagleBone Black (TI Sitara AM3359) and RaspberryPi 3 (Broadcom BCM2837), because their SoCs support interrupts along with the gpiobus driver.
Initially it should be sufficient to wire simple push buttons to the GPIO pins, or to connect them to each other with a simple delay circuit. When everything works fine so far, I am planning to do some more advanced hardware tests e. g. by connecting a Microcontroller for automated long-term scenarios, or even by generating some stress on the pins.
The Code
The source code of the kernel driver can be found in my fork of the FreeBSD src tree on GitHub. The utilities for configuration and testing are located in this repository.
Notes
Usage
Usage of the code is quite simple. First of all, get a handle to the appropriate gpioc device via the gpio_open or gpio_open_device functions of gpio(3). After that set the configuration with an interrupt flag using gpio_pin_config on any pin(s) which should be monitored. The interrupt flags are declared in sys/gpio.h. Please note, that only pins configured as input can utilize interrupt capabilities and only one interrupt flag per pin can be set at once. After configuring the pin(s) with an interrupt flag, simply call read(2) or any other I/O multiplexing syscall on the handle. You can also take a look at gpioc_intr_test of the utilities repository.
Possible further improvements
- Interrupt rate limiting.
- Buffer for occurred interrupts.
- Currently only the last pin is stored in the kernel driver per file descriptor.
- Specific kevent filter.
- Implementing a custom kevent filter brings the advantage that the pin on which the interrupt occurred can be stored directly in the filter-specific data fields of the kevent structure. Thereby no more separate call to read() is necessary.
Questions asked regarding this project
- Does Linux or any other OS already provide an API, and if so does it make sense to be compatible with them or can it be done better?
In Linux this is possible via sysfs. There are virtual files provided for configuration, and also for the actual value of the pin which can be monitored when the pin is accordingly configured. This is no option for FreeBSD, because achieving compatibility with sysfs for this feature seems unsuitable.
I do not know about other operating systems yet, but I think this should be integrated into the already existing utilities in the FreeBSD base system for GPIOs (libgpio and gpioctl) in the first place.