July 25, 2009 posted by Marc Balmer
NetBSD has had support for General Purpose Input/Output devices since the 4.0 release, when the GPIO framework from OpenBSD 3.6 was imported. GPIO devices, or gpios for short, provide an easy way to interface electronic circuits which can be as simple as a LED or that provide more complex functionality like a 1-Wire or I2C bus.
Since the import of the GPIO framework into NetBSD, I have reworked larger parts of that subsystem in OpenBSD to address some problems and drawbacks. I have now imported these changes into NetBSD and continued to improve on them. The new GPIO framework retains backwards compatibility while adding new features: It integrates with the kauth(9) security framework, has it's own config file format gpio.conf(5), and integrates with the system startup scripts in /etc/rc.d.
gpios are either wired internally for a specific function or connected to a header on the system board where electronic devices can be connected. And on many embedded systems LEDs are found that are controlled by gpios, providing a simple user feedback mechanism.
NetBSD's GPIO subsystem is used to control individual pins of gpios, but also a device driver can make use of GPIO pins using the gpio framework. In this case a driver maps the pins it needs and they are no longer available to other uses.
Development goals and motivation
The gpio model found in NetBSD 4.0 is weak in a few regards:
A device driver that needs gpio pins must be configured in a kernel configuration file and a custom kernel needs to be built. Such device drivers therefore can not be attached to gpio(4) devices at runtime.
Since only the user of a machine can know the exact layout of its gpio pins there is always the risk of damaging hardware. The use of gpios can be dangerous and making them generally available can be a risk.
So the goal was to lock down gpio configuration to an appropriate security mechanism and to make it possible to attach device drivers to gpio pins at runtime. As a convenience to users, individual pins could be named for later reference. Backwards compatibility had to be retained, since the older API has made it into a release.
Traditionally UNIX systems had the notion of a securelevel, and in this model all configuration of gpios is done at an early boot stage at securelevel 0 and once the securelevel is raised, it can not be changed. Pins that have been configured at securelevel 0 remain accessible at higher securelevels. NetBSD, however, does no longer directly support the securelevel model, but rather uses kauth(9) as a security mechanism (or, rather hides the securelevel behind kauth(9)). You can still set the securelevel using the kern.securelevel sysctl or by setting securelevel=N in /etc/rc.conf. I you use the securelevel feature by setting it to a value higher than zero, your gpio layout and configuration can no longer be changed once the /etc/rc.d/securlevel script has been run.
A New Syntax for gpioctl(8)
The changes to the kernel parts of course had to be reflected in the gpioctl(8) userland command, which got an all new and easier commandline syntax at the same time.
A device driver that uses gpio pins can now be attached using the following command:
gpioctl gpio0 attach gpioow 4 1
In this example we attach a gpioow(4) device on pin 4 of the /dev/gpio0 device with a mask of 1 (the mask specifies how many and which pins are being used by the driver starting at a offset, which is the pin number.)
If the device had to be detached again, it could be done while still at securelevel 0 using the command
gpioctl gpio0 detach gpioow0
Notice how in the first example, only the driver name is given, but in the second example, the name of the driver instance is specified.
Individual pins can now be configured using a relatively easy syntax and at the same name given a symbolic name. Once a pin has been named, it can be referenced either by pin number or symbolic name.
gpioctl gpio0 set [in|out|inout|od|pp|tri|pu|pd|iin|iout] 
One a pin has been configure this way, it will be accessible in the usual way even when the securelevel has been raised.
While still at securelevel 0, a pin can be unconfigured using the unset command as follows:
gpioctl gpio0 unset
Access to gpio pins is done as follows:
gpioctl gpio0 [0|1|2]
The /etc/gpio.conf configuration file format
To ease the configuration of GPIO pins during system startup I introduced the /etc/gpio.conf configuration file, which is being read by the /etc/rc.d/gpio script if the gpio variable in /etc/rc.conf is set to YES. The configuration file contains of one more lines that follow the gpioctl(8) command line syntax, but without the gpioctl command name. Lines starting with a '#' and empty lines are ignored. In the following examples, we define an input pin, and error led and attach a 1-Wire bus at one pin of gpio0:
# Sample gpio configuration
gpio0 1 set in key
gpio0 3 set out error_led
gpio0 attach gpioow 8 1
Three steps to secure GPIO usage
- Edit the /etc/gpio.conf configuration file and carefully define your GPIO layout
- Set the gpio variable in /etc/rc.conf to YES
- Set the securelevel variable in /etc/rc.conf to 1
These steps will let you use the configured GPIO pins at runtime, but the layout can not be changed, not even by the root user.