Sysfs was used to control GPIOs on Linux system with the GPIOs defined in /sys/class/gpio, but starting with Linux 4.8, a new GPIO interface for user space was introduced, with gpiochip becoming char devices foudn in /dev/gpiochip0, dev/gpiochip1, etc.. , and sysfs allegedly become deprecated.
But a quick check in NanoPi Duo with Linux 4.11 shows both GPIO user space interfaces appear to be enabled:
1 2 3 4 |
ls /sys/class/gpio/ export gpiochip0 gpiochip352 unexport ls /dev/gpiochip gpiochip0 gpiochip1 |
Nevertheless overtime, sysfs will die out, and the new subsystem will likely be used by all systems, so it might be useful to learn more about it.
One way to do that is to watch Bartosz Golaszewski’s ELCE 2017 talk entitled “New GPIO Interface for User Space” with the video embedded below. But I first I’ll summarize some of the key points.
Now GPIO handling from user space becomes similar to other char driver with ioctl, poll and read functions, and beside assigning numbers to GPIOs you can assign names. The API (in linux/gpio.h) provides access to chip info, line info, line request for values, reading values, settings values, line request for events (rise/falling edges), polling for events, and reading events. Bartosz goes into details for each function in his talk.
Since the kernel API is a bit complicated to use, there’s also a C library called libgpiod, which comes with some tools like gpiodetect, gpioinfo, gpioset, gpioget, gpiofind & gpiomon. Further work includes C++ and Python bindings, as well as a GPIO daemon and client. Example code for libgpiod:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
struct gpiod_chip *chip; struct gpiod_line *line; int rv, 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 } rv = gpiod_line_input(line, “foobar”); if (rv) { gpiod_chip_close(chip); return -1; } value = gpiod_line_get_value(line); gpiod_chip_close(chip) |
Some example of user space tools found in libgpiod:
1 2 3 4 5 6 7 8 9 10 11 12 |
gpiodetect gpiochip2 [gpio-mockup-C] (8 lines) gpiochip1 [gpio-mockup-B] (8 lines) gpiochip0 [gpio-mockup-A] (8 lines) gpioset gpiochip1 3=1 gpioget gpiochip1 1 2 3 4 5 0 0 1 0 0 gpiomon gpiochip0 2 event: RISING EDGE offset: 2 timestamp: [1508094667.935877214] |
The first command return the list of gpio chips, their names, and number of lines, the second set the 3rd pin of Chip 1 to high, the third return the values of 5 different pins, and the last one monitor a pin to detect an event and return the time when it happened.
You can check out the latest libgpiod source code, or download the latest stable release of the code as a tarball. libgpiod is already available in meta-openembedded & buildroot, and packaged in Fedora and Arch Linux.
You may also be interested in the slides.
Jean-Luc started CNX Software in 2010 as a part-time endeavor, before quitting his job as a software engineering manager, and starting to write daily news, and reviews full time later in 2011.
Support CNX Software! Donate via cryptocurrencies, become a Patron on Patreon, or purchase goods on Amazon or Aliexpress
I guess the speed will be much higher than using sysfs. Could you do some comparison?
@danman
There’s little point to benchmarking performance seeing how the previous interface was dropping events while context switching left and right.
I just wish they could have used a more human-friendly protocol so we could script without having to use a C library or a user-space program.
Cool improvement i like event triggering and call by name, C library well documented.
But i didn’t see any interrupt on change
I was in the room in Prague last week.
He should really change the name of this package, it contains a library, but also userspace tools. This is really confusing.
But this new char device could open the way to faster gpio access, like for JTAG-over-GPIO interfaces, which are now only available to Raspberrypi devices in openocd but this is specific to bcm2835 chips, not portable to allwinner or any other devices. Now let’s hope someone writes a proper driver for openocd for that purpose. Those Allwinner OrangePi zero are so cheap that it does not make sense bothering with FTDI dongles anymore. Urjtag and openocd also have sysfs cable support, but they are much slower.
@zoobab
I think many JTAGs are timing sensitive so the scheduling (not real time) will still be a blocker for most.
@RK
If applying PREEMPT_RT patch is sufficient then it’s easy to use those inexpensive Allwinner thingies for this purpose: https://forum.armbian.com/topic/5576-fully-preemptible-kernels/
@tkaiser
Yeah the preempt patches combined with this should work better on 1:1 instructions-to-cycles cores like older ARMs and current MIPS. Though I suspect you’d still need to dedicate a core to the thread and run the system under a fairly embedded rootfs since many linux daemons tend to fork like crazy and real time scheduling means losing cycles like there’s no tomorrow over this.
But hey, if people are using RasPi’s GPIOs to R\W EEPROMs, I’m sure there’s a lot of diagnostic jtags you can make due with.
@RK
FEL booting Allwinner devices via the USB OTG port works really nice. We added this to Armbian’s build system last year (FEL+NFS, everything set up automatically) and I do ‘full OS image’ testing since 18 months with zero delay and no SD cards harmed 😉
I’ve created a github project called libgpiod-extra that automates the build process on Armbian and generates Python bindings. Only a couple of distributions include this currently, so you have to build it. I also included instructions to built it manually, then you can generate the Python bindings. No need to wait for the official release!
https://github.com/sgjava/libgpiod-extra
https://github.com/sgjava/userspaceio
After a lot of work and testing I have produced the User Space IO project! It provides Python 3 and Java 8 bindings for Linux user space GPIO, SPI, I2C and Serial interfaces. This allows cross SBC and cross language development using a common API. I took two best of breed C APIs for Linux user space libgpiod and c-periphery and produced CFFI bindings for Python and JNA bindings for Java. Since all the bindings closely match the C API it’s easy to move from language to language. The side effect of using the Java library is that it should work with many different JVM based language. How about creating your programs in Kotlin or Scala for instance?
GPIO access uses the new gpiod interface and not the deprecated sysfs interface using libgpiod v1.1 (head from git repo). GPIO, SPI, I2C and serial interfaces expose all the low level functionality, but I have added some helper methods for handling repetitive functions like building I2C messages, reading words from two registers, etc. You can of course go as low level as you need to. Please use Github to post any issues, pull requests, etc. I have tested Nano Pi Duo for 32 bit and NanoPi Neo +2 for 64 bit compatibility. I’ll test more SBCs as I have time. Also, I have deleted https://github.com/sgjava/libgpiod-extra since User Space IO supersedes it.
I think the example is outdated because gpiod_line_input() does not appear to exist.