Linux kernel GPIO user space interface


Since version 4.8 the Linux kernel has a new interface based on character devices for accessing and managing GPIO lines in user space.

Linux kernel GPIO interface

GPIO stands for General-Purpose Input/Output and is one of the most commonly used peripherals in an embedded Linux system.

Internally, the Linux kernel implements the access to GPIOs via a producer/consumer model. There are drivers that produce GPIO lines (GPIO controllers drivers) and drivers that consume GPIO lines (keyboard, touchscreen, sensors, etc).

To manage the GPIO registration and allocation there is a framework inside the Linux kernel called gpiolib. This framework provides an API to both device drivers running in kernel space and user space applications.

Linux kernel GPIO user space interface

The old way: sysfs interface

Until Linux version 4.7, the interface to manage GPIO lines in user space has always been in sysfs via files exported at /sys/class/gpio. So for example, if I want to set a GPIO, I would have to:

  1. Identify the number of the GPIO line.
  2. Export the GPIO writing its number to /sys/class/gpio/export.
  3. Configure the GPIO line as output writing out to /sys/class/gpio/gpioX/direction.
  4. Set the GPIO writing 1 to /sys/class/gpio/gpioX/value.

As a practical example, to set GPIO 504 from user space, we would have to execute the following commands:

# echo 504 > /sys/class/gpio/export
# echo out > /sys/class/gpio/gpio504/direction
# echo 1   > /sys/class/gpio/gpio504/value

This interface is very simple and works pretty well, but has some deficiencies:

  • The allocation of the GPIO is not tied to any process, so if the process using a GPIO ends its execution or crashes, the GPIO line may remain exported.
  • We can have multiple processes accessing the same GPIO line, so concurrency could be a problem.
  • Writing to multiple pins would require open()/read()/write()/close() operations to a lot of files (export, direction, value, etc).
  • The polling process to catch events (interrupts from GPIO lines) is not reliable.
  • There is no interface to configure the GPIO line (open-source, open-drain, etc).
  • The numbers assigned to GPIO lines are not stable.

The new way: chardev interface

Since Linux version 4.8 the GPIO sysfs interface is deprecated, and now we have a new API based on character devices to access GPIO lines from user space.

Every GPIO controller (gpiochip) will have a character device in /dev and we can use file operations (open(), read(), write(), ioctl(), poll(), close()) to manage and interact with GPIO lines:

# ls /dev/gpiochip*
/dev/gpiochip0  /dev/gpiochip2  /dev/gpiochip4  /dev/gpiochip6
/dev/gpiochip1  /dev/gpiochip3  /dev/gpiochip5  /dev/gpiochip7

Although this new char device interface prevents manipulating GPIO with standard command-line tools like echo and cat, it has some advantages when compared to the sysfs interface, including:

  • The allocation of the GPIO is tied to the process that it is using it, improving control over which GPIO lines are been used by user space processes.
  • It is possible to read or write to multiple GPIO lines at once.
  • It is possible to find GPIO controllers and GPIO lines by name.
  • It is possible to configure the state of the pin (open-source, open-drain, etc).
  • The polling process to catch events (interrupts from GPIO lines) is reliable.

Library and tools

To use this new char device interface there is a library and a set of tools provided by the libgpiod project.

For example, the following program in C is using libgpiod to read a GPIO line:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void main() {
	struct gpiod_chip *chip;
	struct gpiod_line *line;
	int req, value;

	chip = gpiod_chip_open("/dev/gpiochip0");
	if (!chip)
		return -1;

	line = gpiod_chip_get_line(chip, 3);
	if (!line) {
		gpiod_chip_close(chip);
		return -1;
	}

	req = gpiod_line_request_input(line, "gpio_state");
	if (req) {
		gpiod_chip_close(chip);
		return -1;
	}

	value = gpiod_line_get_value(line);

	printf("GPIO value is: %d\n", value);

	gpiod_chip_close(chip);
}

The library is in C but there are bindings for other languages like C++ and Python.

If you want to manage and interact with GPIO lines from a terminal, you can use the command-line tools provided by libgpiod.

The gpiodetect command will list all GPIO chips, their labels and the number of GPIO lines:

# gpiodetect
gpiochip0 [209c000.gpio] (32 lines)
gpiochip1 [20a0000.gpio] (32 lines)
gpiochip2 [20a4000.gpio] (32 lines)
gpiochip3 [20a8000.gpio] (32 lines)
gpiochip4 [20ac000.gpio] (32 lines)
gpiochip5 [20b0000.gpio] (32 lines)
gpiochip6 [20b4000.gpio] (32 lines)
gpiochip7 [mcp23s08.0] (8 lines)

The gpioinfo command will print information about GPIO lines of a specific GPIO controller (or all GPIO controllers if none are specified):

# gpioinfo 0
gpiochip0 - 32 lines:
        line   0:      unnamed       unused   input  active-high
        line   1:      unnamed       unused   input  active-high
        line   2:      unnamed       unused   input  active-high
        line   3:      unnamed        "scl"  output  active-high [used open-drain]
        line   4:      unnamed       unused   input  active-high
        line   5:      unnamed       unused   input  active-high
        line   6:      unnamed        "sda"  output  active-high [used open-drain]
        line   7:      unnamed "Push Button"  input  active-low  [used]
        line   8:      unnamed       unused   input  active-high
        line   9:      unnamed          "?"  output  active-high [used]
        line  10:      unnamed       unused   input  active-high
        line  11:      unnamed       unused   input  active-high
[...]

The gpioset command will set a GPIO line. For example, the following command will set the GPIO line 0 of the gpio controller 7:

# gpioset 7 0=1

The gpioget command will read the value of a GPIO line. For example, the following command will read the GPIO line 0 of the gpio controller 7:

# gpioget 7 0
1

The gpiomon command will wait for events on GPIO lines and print them to standard output:

# gpiomon 0 7
event: FALLING EDGE offset: 7 timestamp: [    4564.943435210]
event:  RISING EDGE offset: 7 timestamp: [    4565.072156210]
event: FALLING EDGE offset: 7 timestamp: [    4566.113641877]
event:  RISING EDGE offset: 7 timestamp: [    4566.366691877]

The source code of all these commands is available at the libgpiod repository.

As you can see, the new Linux kernel GPIO user space interface has a very simple, elegant and robust API, and should be used on embedded Linux development from now on.

About the author: Sergio Prado has been working with embedded systems for more than 20 years. If you want to know more about his work, please visit the About page or Embedded Labworks website.

Please email your comments to sergio at embeddedbits.org or sign up the newsletter to receive updates.