March 28, 2013
The Raspberry Pi (RPi) is a credit card sized computer that is versatile and affordable. Anyone that has experimented with the General Purpose Input & Output (GPIO) pins on the RPi may have noticed that programs must be run as root (with sudo) to access the GPIO pins. This is rather bothersome because running programs as root defeats the Linux security model which is one of the many benefits of the Linux operating system.
Each GPIO pin is mapped to specific memory location, much like most embedded systems. Since all of the RPi's peripherals are mapped this way, changing the wrong bit can cause unrecoverable errors. The Linux kernel protects users from these errors by requiring that programs be run as root to access this segment of memory. Many solutions exist which allow access to this segment of memory. One such solution maps the memory to a "/sys/class/gpio/" interface. This method has its advantages but the main disadvantage is the overhead of system calls which add unnecessary lag to modifying operations.
The Linux kernel should completely insulate programs from the implementation details of physical memory. The version of the kernel that comes from the Raspberry Pi Foundation isn't equipped to provide safe access to the GPIO pins. This new custom kernel module will plug into the Linux kernel to arbitrate and manage access to the GPIO pins so that user programs can access the GPIO pins safely.
In an effort to develop a kernel module to manage the RPi's GPIO pins, many methods of kernel userspace communication were examined. The standard read/write interface doesn't really make sense because the values of the GPIO pins are non-seekable values, and their data doesn't come in arbitrary sized chunks. For these types of drivers the ioctl system calls are provided.
For ease of use the driver provides ioctl calls to read, write, request, free, toggle, set, clear and change mode of the available GPIO pins on the RPi. Any process can read any pin because reading is a non-modifying operation. The driver implements a pins array where the index represents the pin number and the value stores state information about the pin to mitigate race conditions between processes trying to control the same pin. Each pin has three possible states: 1. the pin is reserved for system use, 2. the pin is reserved by a process, 3. the pin is available for reservation. Pins that are in either reserved state, can not be requested (or checked out) by any process for write access because doing so could cause an unrecoverable error or interfere with the workings of other built in peripherals.
When a pin is requested by a process, the driver ensures the pin requested is in the range 0-31, then looks up the state in the pins array. If the pin is not reserved, the state value for that pin is replaced with the requesting processes id (PID). The state values are 32 bit unsigned integers, to represent the two types of reserved state, 0 is used for unassigned, and 1 is used for unassignable. These values were picked with much caution, PID 0 is reserved for the the system idle process and PID 1 is reserved for the system init process. These processes will never make ioctl requests to the driver so they can be used to identify special cases. Any PID between 0 and 300, which are reserved for the kernel should work.
An array is used because it is the minimal data structure needed to store the state information using the minimal amount of memory and requiring the least amount of overhead when attempting to lookup required information.
To ensure that the driver does not cause unrecoverable errors, a number of tests were performed.
Included with the driver are programs which test, access to all available pins, output on all pins, input on all pins, colliding requests where two processes attempt to request the same pin and reading while another has write access.
To verify that the collision between clients requesting the same pin will be gracefully resolved, a fort test is employed. This test forks to create a copy of itself then attempts to request a number of pins. Figure 1 shows how Client 2 beat Client 3 to pin 4, now that Client 3 has exclusive access, it can modify attributes of the pin, while Client 2 gets an error message. Client 1 is only reading information so it doesn't need exclusive access.
Other tests included ensure that clients have access to all available pins by requesting each pin, and performing each ioctl operation on them. To ensure that all pins functioned as inputs, the testing is more manual, on the breadboard each pin was tied high and low and its value read in both states.
The GPIO kernel module provides an interface to read, write and control access to the RPi's GPIO pins. The modules locking mechanism ensures user programs don't cause unexpected results.