Pi IoT In C Using Linux Drivers - GPIO Character Driver
Written by Harry Fairhead   
Monday, 29 March 2021
Article Index
Pi IoT In C Using Linux Drivers - GPIO Character Driver

Linux drivers make working with devices so easy - assuming you know how. The most basic of all hardware is the GPIO and the sysfs way of working is now obsolete. Find out what the new way of doing things is all about.

This content comes from my newly published book:

Raspberry Pi IoT In C Using Linux Drivers

By Harry Fairhead


Buy from Amazon.


  1.  Choosing A Pi For IoT

  2. C and Visual Studio Code

  3.  Drivers: A First Program

  4.  The GPIO Character Driver
         Extract: GPIO Character Driver

  5. GPIO Using I/O Control

  6.  GPIO Events

  7.  The Device Tree
        Extract: The DHT22

  8.  Some Electronics

  9.  Pulse Width Modulation
    Extract:  The PWM Driver 

  10. SPI Devices
    Extract: The SPI Driver 

  11. I2C Basics

  12. The I2C Linux Driver ***NEW!


  13. Advanced I2C

  14. Sensor Drivers – Linux IIO & Hwmon
      Extract: Hwmon  

  15. 1-Wire Bus
      Extract: 1-Wire And The DS18B20 

  16. Going Further With Drivers

  17. Appendix I



As far as IoT goes, the fundamental Linux driver is the GPIO driver, which gives you access to the individual GPIO lines. This is another built-in driver and so is like the LED driver introduced in the previous chapter, but it is a character driver and used in a slightly different way.

In most cases, when a device is connected via a set of GPIO lines, you usually use a specific device driver. In this way you can almost ignore the GPIO lines and what they are doing. However, there are still simple situations where a full driver would be overkill. If you want to interface a button or an LED, then direct control over the GPIO lines is the most direct way to do the job even if there are Linux LED drivers – see the previous chapter – and even an input driver.

Until recently, the standard way to work with GPIO in Linux was to use the sysfs interface. You will see a lot of articles advocating its use and you will encounter many programs making use of it. However, GPIO sysfs was deprecated in Linux 4.8 at the end of 2016 and is due for removal from the kernel in 2020. Of course, it takes time for Linux distributions to make use of the latest kernels. At the time of writing Pi OS is using Linux 4.19, released two years earlier. Nevertheless, sysfs GPIO will soon be removed and while you still need to know about it to cope with legacy software, you shouldn’t use it for new projects. You can find out how it works in Appendix I of Raspberry Pi IoT in C, Second Edition, ISBN:9781871962635.

Its replacement is the GPIO character device and, while this looks superficially like the old sysfs interface, it has many major differences. While it has some advantages, it also is slightly more complex and can no longer be used from the command line – it is a program-only interface. This said, there are some simple utility programs that are fairly standard and allow GPIO control from the command line. These are covered in the first part of the chapter, even though they are unlikely to be the main way that you work with the new interface. There is also a wrapper library called gpiod. Although it isn’t necessary for simple access to the GPIO lines, it is described in this chapter.

GPIO Character Device - GPIOD

The new approach to working with GPIO comes pre-installed in the latest version of Pi OS, but it isn’t supported in earlier versions. If you look in the /dev directory you will find files corresponding to each GPIO controller installed. You will see at least:


This represents the main GPIO controller and all of the GPIO lines it provides. If you know how sysfs works you might well be expecting instructions on how to read and write to the file from the console. In this case, reading and writing to the file will do you little good as most of the work is carried out using the input/output control system call, ioctl() which cannot be used from the command line. The use of ioctl is typical of a character driver, but it does mean that using the GPIO driver is very different from other file-oriented drivers described later. The next chapter looks at the use of ioctl to directly control the GPIO character device.

If you want to explore the GPIO from the command line, you need to install some tools that have been created mainly as examples of using the new device interface. To do this you need to first install them and the GPIOD library that you will use later:

sudo apt-get install gpiod libgpiod-dev libgpiod-doc

Notice that, if you don’t want to use the library to access the driver, you don’t have to install it – the GPIO driver is loaded as part of the Linux kernel and is ready to use.

In book but not in this extract.

  • The Utilities

Installing The GPIOD library

The GPIOD library is installed along with the tools discussed at the start of the chapter, but you don’t have to use it if you are happy with the ioctl system calls described in the next chapter.

The library splits into two parts, the context-less functions and the lower-level functions, which can be considered context-using. In nearly all cases you will want to use the lower-level functions as the contextless functions have some serious drawbacks. Let’s take a look at each in turn.

To use the library you will need to, if you haven’t already done so, install it:

sudo apt-get install gpiod libgpiod-dev libgpiod-doc

You also need to add the headers:

#define _GNU_SOURCE
#include <gpiod.h>

and you will need to load a library file.

If you are using VS Code you will need to edit settings.json to load the file:

    "sshUser": "pi",
    "sshEndpoint": "",
    "remoteDirectory": "/home/pi/Documents/
    "std": "c99",

adding "libs":"-lgpiod". By editing the settings.json file in the top level folder, the setting will apply to all of the subfolders. If you don’t want this to be the case, you need to look into VS Code Workspaces, which allow you to set individual settings in each folder.

If you see error messages in VS Code concerning the new header file, you can either ignore them or copy the ARM headers into a sub-folder using the copyARMheaders task. This copies all of the standard headers to a sub-folder called include on the local machine. Notice that this is only necessary to satisfy the local machine’s error checking - the program will run on the remote machine without any problems as long as the header is available there.

If you want to do the same job on the command line, add the option:


The Contextless Functions

The contextless functions all have ctxless in their names and they work by opening the necessary files, performing the operation and then closing them again. This means you don’t have to keep track of what files are open and what GPIO lines are in use, but you have the overhead and side effects of repeatedly opening and closing files.

There are two simple get/set functions:

gpiod_ctxless_get_value(“device”, offset, active_low,
consumer”); gpiod_ctxless_set_value(“device”, offset, value,
active_low,”consumer”, callback, param)

where device is the name, path, number or label of the gpiochip, usually just 0, and offset is the GPIO number of the line you want to use, value is 0 or 1 according to whether you want the GPIO line set high or low, and active_low sets the state of the line regarded as active. If set to true, 1 sets the line low and vice versa. You usually want to set this to 0 so that 1 sets it high.

Replace consumer with the name of the program/user/entity using the GPIO line. The attributes callback and param work together to define a callback function and the parameters passed to it. The callback function is called immediately before the line is closed. The get version of the function returns the current state of the line as a 0 or 1 according to the line state and the setting of active_low.






Last Updated ( Saturday, 03 April 2021 )