 So my name is Bartosz Golaszewski, I'm a consultant. I mostly work in the field of embedded Linux. I've been doing this for the last eight years and I also contribute to a range of projects related to embedded Linux. And my talk today is going to be about GPIOs in user space. So I'm going to start out by giving a brief overview of what GPIOs are, even though I suspect most of the people to know it already. Then I'm gonna describe how the kernel interfaces look like and then I'll move over to the legacy CISFS interface. And finally I will talk about the new character device-based interface and a user space library and tools that will help you use it. So GPIO stands for General Purpose Input Output. This is a generic pin with a no predefined function and this can be configured at runtime. So what can you do with a GPIO? So we can enable it in disable it obviously. Then we can set the so-called direction which is a mode in which the GPIO works. So it can be output where we actively drive the line and input where we read the values where somebody else is driving the line. And an extension of the input mode is the fact that we can of course if the hardware allows it we can make a GPIO to be a source of interrupts so we can be notified about about the changing state. So where do we use GPIOs? So mostly these are used wherever a simple very simple kind of communication is required. So basically the values we can read are low that the line is driven low or high. So for input mode this would be buttons where the user presses a button we get uninterrupted and can read the value if it's pressed or released. It's various level detectors, thermostats, so whenever we want to be informed by some level change about level change. And then when we are the ones who actively drive the line so when we do the output this can be various LEDs, buzzers, power switches, relays, stepper motors for robotics. We can implement things like GPIO bitbanging with software serial port with the help of GPIOs. And we have this notion of providers and consumers of GPIOs. So GPIOs are usually provided by SOCs, dedicated devices that expand the number of GPIOs available. So we will call them expanders. And various multifunction devices also provide, usually also provide some GPIOs for things like reset lines, etc. So this notion of a provider and consumer is modeled in the kernel. We have a two-part framework for the GPIOs providers which lives in the driver's GPIO directory. And then we have an API for consumers. So in general all the providers live in this directory whereas all the consumers can be, since the consumers can be any pretty much any driver they are all over the place. And for the consumers we have two coexisting frameworks. So one of them is a legacy framework. It's based on a continuous global number space where each GPIO pin has a unique number assigned in the kernel. This is a wrong approach in that GPIOs are a two-level hierarchy where we have GPIO pins associated with GPIO chips whereas this number space doesn't know this hierarchy. So in order to have a better model of GPIOs we have a second most more recent framework for consumers where GPIOs are associated with devices that may want to use them. So the example is in the old framework you would have to call a function called GPIO request I think and pass it a global number. But this number can change because we can have dynamic expanders that are instantiated later in the boot process so this number may change and this isn't really very reliable. So the new framework, the modern approach is to, I'll use an example of device tree, is to take like the providers will tell, like we will have a special node in the DT which will determine, like which will define all the pins exported by a provider. We can even give them names so that it's going to be easier to retrieve them. And then the consumers in the device like in the DT nodes of the consumer devices we associate these GPIOs, the ones that are relevant for our device with the consumer. And later in the driver code the driver can retrieve this GPIO by its index or by a name. So this gives you a lot more fine grained control over how we use the GPIOs and how we make them available to drivers. So, yeah, and both frameworks for the consumers support device resource so there's no need to free them when the device is being turned down. So the general idea is to always write drivers in the kernel that use GPIOs but sometimes it's not possible and we may want to interact with GPIOs from the user space. So this usually, the example use cases for that is, for example, power switches and relays. So I had that problem some time ago when I was working on a project that in the kernel there is no framework currently for power switches and relays. It doesn't exist so you're kind of stuck with the GPIO with the user space approach. And then there are certain devices that communicate or, for example, some GPSs and Bluetooth devices they communicate over a serial port and then they export GPIO lines for things like reset or other simple functions. And also it came to my attention that after speaking with a couple of people involved with intelligent home systems it turned out that they like to implement everything in user space and then, for example, toggle the GPIOs to close window blinds, et cetera. And another area in which we use GPIOs from user space are robotics. So the legacy user space interface lives in SysClass GPIO and actually I was not aware of that. Someone during the conference told me that this interface was merged during the time when the GPIO subsystem did not have an active maintainer. So this might explain why it's not very well thought. So this is the comet in which the system was merged. And another interesting thing is that it's quite young. I remember first using this interface in 2009 and it turned out today when I looked up the Git history that it's been only merged in 2008. So back then I had thought that it must have existed forever but apparently it's pretty recent. So with this interface there are quite a number of problems. So first of all, this is a global interface. It lives in SysFS. So although we can affect things like permissions, we can change the permissions of these files, but we can't really take ownership of GPIO chips and GPIO lines because another privileged user can always come and change our GPIOs unless we really tweak the files and permissions. Then the state is not tied to any process using it. So that means that if we have a program that will come and start tweaking the GPIOs, export them and it crashes and I don't know, doesn't get restarted or doesn't really, isn't really prepared for that, then the state remains exported because in the kernel, if you will look at the code in the kernel, the SysFS interface is just another user of the legacy API in the kernel. So when you write, how many of you have used this SysFS interface? Yeah, so almost everybody. So you know that you go to SysClass GPIO and then you write an absolute number to export and a file appears and you go inside and you have a number of attributes that you can write to or read from in order to interact with GPIOs. So when you look in the kernel, when you write a number to the export file, it just calls GPIO request with the given number. So this already makes this interface bad in the sense that it uses the legacy interface. So the API, the API is not very convenient because you need to pay attention to a number of files. You can do things like, for example, you can pull GPIOs for events, but this is completely unreliable with SysFS because what you do in order to wait for events, you set a file that's called edge to whatever edge of events you're waiting for and then you pull, you call the pull function on the value for this specific line. But when an incoming, like the read event occurs, you have to either else seek to the beginning of the value file or close and reopen it. During that time, another event can appear and it's not being queued, so you're going to lose events. Another thing is, if a GPIO line has a name in the kernel and you export a line that is named, the file, after you write to export, a file that will appear in the directory will have the name of this line. By default, it will be called GPIO and the absolute number of the GPIO. This makes, for example, looking up lines by name very complicated because you have to export or try to export every single line, check the name of the directory that appeared and unexport it. So, yeah, a lot of problems with this interface. This has led to starting some work on introducing a new user space interface for GPIOs. This was finally merged in Linux v4.8. This is a bit similar, I'd say, to how the iSquad C user space interface works. Now we have a character device, a single character device for every GPIO chip. This lives now in depth, obviously. And now, to interact with GPIOs, you only need a set of system calls. So, basically, all you need is open, IRCTL, pull, read, and close. So, this interface brings in a lot of new features. So, you can now request multiple lines at once with a single IRCTL. You can set and get read values of lines with a single IRCTL as well, with a single context switch. Now, it's very easy to look up GPIOs by names, also chips, by names or labels, or however they are identified in the kernel. The support has been added for new features. For example, we can now specify from the user space if a GPIO line that we want to drive in an output mode is connected as open source or open drain line, which affects the way we will drive it. Then, we have a way now to identify the current consumer of a line. Because if someone else has ownership of a line, it can set the consumer string when requesting it. And we can pull it out from the kernel so that we know who is using a line, be it in the kernel or in user space. And now, the character device will now work with UDef. So it appears through the UU event interface. We can specify UDef rule so that permissions will be set accordingly. And, yeah, since we now use open, we have a file descriptor associated directly with the line so that pulling is much more straightforward. And the events are queued so that we can read them one by one. So the character device ABI lives in, I don't know why I use the API, it should have been ABI, lives in linuxgpio.h in the Linux UAPI section. So the driver is logically, sorry, the driver, the header is logically split into several parts. So we have the chip info operations, line info operations, we can request lines for values for reading them or setting them. And then we can request lines for reading events and read the events themselves. So for each of the operation, there is usually a data structure or more data structures and a couple IOCTLs. So I'm going to go through these operations. So first of all, the first thing that you usually want to do is to get information about a chip. So you open a device using the open system call. The device lives in, for example, in this example, it's def GPIO chip zero. And you have a structure defined in the header I showed before, so the structure is defined in the header. And then you open the file, you store the file descriptor, and you just run the IOCTL, the chip info IOCTL, while passing the infrastructure as argument. This will fill the structure with name, the label, and the number of the lines. So this is quite simple. And now the second operation that you usually want to do is to get info about the line. Yeah? I'm sorry. Yes, yes, because, oh, no, no, the direction we're going to set after that. This only opens the device in read write mode. The open itself doesn't do anything. This just opens the chip. So on the chip, there are several lines. The lines are associated with a chip. So now we just open the device, the device file, and you run an IOCTL that retrieves the information for us. So it fills this structure with information about the chip. So now, so far, we haven't touched the lines. So this is just the info about the chip. So, for example, in this case, the chip's name will be GPIO chip zero, and the label will be whatever we call it. And the number of the lines will contain the number of the lines associated with this chip. So this is the hierarchy I was talking about. So you have, after you call the IOCTL, the number of lines appears in this structure, and you know it, like the kernel knows it either from DTE or from board files or from ACPI. There are multiple ways to tell the kernel how many lines are there on the chip. So now this one is about the line. So if you want to get the information about each GPIO line associated with a chip, you have to call for each of it the GPIO get line for IOCTL. So you have the GPIO line info struct, and what you do is you just set, you memset it. It's not mandatory, but well, let's initialize it. And then let's just set the offset of the line we want to know something more about. So here we want the third line, like the fourth line, so the index three counted from zero of the line associated with this chip. So this FD here is the file descriptor we got previously from the previous IOCTL. So is this clear in general? So now it gets interesting. Yeah, so this structure gets filled again by this IOCTL. So the only thing we set is the line offset, and then the kernel tells us what the flags of this line are, and its name and consumer. So a line may have a name, it's not mandatory, and it may have a consumer. If it's already in use, it's going to have a consumer defined. So now it gets interesting. This is where we set the direct, yeah, you were going to get a question mark. So, sorry. So the question was if there's no consumer, what are we going to get? It's going to be a question mark. Oh, yeah, I'm going to define, am I? Oh, yeah, sorry. I forgot about the flags. Thank you. So the question is what are the flags? So yeah, the flags are defined here. This is the variable. So flag kernel means it's being used. It's not very obvious, but this flag means that it's being used, not necessarily by the kernel. It might be used by the user space, but the flag is going to be, I'm going to say kernel. Then this is the direction. So if this flag is set, the direction is output. If this flag is not set, the direction is input. This is the active state, so the potential inversion of the values. And then open drain and open source, this tells us if the kernel knows how the line is connected, if it's open source or open drain, it's going to be reflected in this flag. So yeah, so now the request. What we do, if we want to request the lines, if we want to take ownership of the lines, we have to fill this structure here with a number of line offsets. So we can specify up to GPIO handles max, which is 64 lines. We fill this array with offsets of the lines we're interested in. Then we set the flags. So now the flags. This is how we set the direction. So the flags, we have to set the flag, the input or output flag. Then we can specify if we want to invert the logic. Potentially we can also specify the open drain and open source flags from the user space if, for example, the kernel doesn't have this knowledge. Yeah, and if, so what do we do? In this example, we want to request the lines in the output mode. So we want to drive the lines. So we set, we take the structure. We set the flags to GPIO handler request output. Then we say that we want two lines. So number of lines is going to be two. And then we set the offsets. It's going to be three and five. And then we set the default values for these lines. In this case, it's going to be one and zero. This is the consumer label. So we set it to foobar. We call the IOS with the previously retrieved FD. And what happens now? This FD is being filled by the kernel. Kernel creates a new file descriptor for an anonymous I node. And this FD is being set to the value. So if we now want to set or get the values I'm going to talk about in the next slide, we use this FD. No longer this FD retrieved by the first IOS et al. Yeah. So this is how we set or get values. So in the previous example, we set it to output. So we're going to set it. This is a very simple structure. Again, we have just the values. And these values correspond with the lines that we previously requested. So we set the values to some new values. In this case, we invert the values. And again, this time, we use this FD. So the FD that we got here. And again, we call the IOS et al. Either the set line or get line. The difference is that for get line, this structure is going to be filled for us by the kernel. Whereas here, we need to fill it ourselves and pass the data to the kernel. So is that clear? How do we do it? So yeah. So I'm going to accelerate. Yeah. So now the events. This is something interesting. So if we may want to, instead of setting and reading values, we may want to set it to input mode and passively wait to be notified about events using the poll operation. So again, this time, we also have to retrieve the FD from the file descriptor from the kernel. But this time, it's one file descriptor per line, which is obvious because we need to know which line raises an event. So again, we take the GPIO event request structure. We set the line offset. We set, in this case, we set the direction explicitly to input, but the output direction will not be accepted by the kernel because obviously this couldn't work. We wait for both edges events. We can specify if we only want to be notified about rising edge or falling edge events. Again, consumer label and IOS et al. This IOS et al is again called on the descriptor, on the chip descriptor, so on the first one from the operation which I showed on the first page. What's happening now? What's happening now? This FD gets set by the kernel and we can now poll it for poll in and poll pry events, which is showcased here. So we poll it and when an event occurs, we read an event, we read the data to a buffer that is this structure. What kind of information do we get from the kernel? We get the time stamp of the event and an ID, so we know if this is a rising edge event, if the value changed to high or the falling edge event, when the value changed to low. Yeah, so I want to talk because I understand that this API is super complicated. It's not super complicated but it's not very convenient. So the first question, when I first heard about this interface, the first question I asked myself was how do we get people to use it because this is like the CISFS interface has an obvious advantage over this one that it's just convenient to use because you don't have to write a C program to toggle the GPIOs. So yeah, so for this, there is a project called lead GPIOD and my involvement with this project has a history of around one year. So last time during the ELCE in Berlin, I was there was a talk by Linus Valet, who is the maintainer of the GPIO sub system in the kernel and while the talk was about the new things in the kernel API, Linus briefly mentioned the GPIO device, the character device as a new recommended API and the API for the user space and that the CISFS is being deprecated and the end goal is to remove the CISFS completely because this is the only way to remove this global number space from the kernel, which is the goal for the far future. And that time, about a year ago, I was working on the bailibre ACME CAPE, on the power measurement CAPE and we were switching to the industrial IO sub system in the kernel to using the industrial IO for the measurements and these ACME probes, I don't know if you saw them, they have power switches, like external power switches which are connected to GPIOs in an expander and we wanted to manipulate these power switches, so turn them on and off and we tried, like my first idea was to integrate it with the industrial IO system, sub system, but this was shut down because this has nothing to do really with industrial IO. So I was told back then that I should try with the regulators, so I submitted a patch to the regulators but I wasn't aware that apparently this is a no-go, so any kind of user space involvement with the regulator is no-go, so this was nacked as well and I think I was told by someone to just stick to GPIOs and then I saw the talk from Linus and learned about the GPIO character device and I decided to write a, because obviously this was super complicated, I decided to write a set of tools inspired I'd say a bit by iSquad C tools. Do you know iSquad C tools? For interacting with the iSquad C device, so I was kind of inspired by this. I decided to write a small project for that and also to include a user space library with a simplified C API that will be easier to use and that will better keep track of what's happening behind the scenes, keep track of all the fighter scripters and whatnot. So I released the first version in January. This was written very fast, so I tried to stick to I don't know if it's GNU standards or whatever, but for example, the major release should be stable, so I released it, but I was not very happy with the API since it was very rushed, I wrote it in a week or so and released it. So the latest stable version is 0.3.1, but I'm working on a 1.0 release and the API has been completely reworked, so it's quite different from what you're going to see in 0.3.1. Also, this API would need to review, so I would really appreciate it if you could look at it and send me an email about what you think because I don't want to release something that's going to turn out to be not very good again. And what's inside? So we have a C API, fully documented in Doxygen. I'm going to show some examples. I'm not going to go into much detail though because I think the documentation is quite detailed. And with the library, come a set of command line tools because this is something that's going to force you the most, so it's a way to script your GPIO operations. So there's a set of tools and we have a custom test suit that works together with the GPIO, it's actually GPIO mockup kernel module. We have this testing module in the kernel which exposes like dummy GPIO chips. Recently it's been extended, now it has things like an interrupt simulator, so you can actually simulate interrupts and see if your polling operations still work. And this test suit is custom because I needed to work with a kernel module, so no I thought initially about using some Google test or something but in the end I ended up writing my own thing. So with this test suit we can also test the kernel API. So as soon as some regression happens in the kernel we should figure it out by just running the test suit. So the C API is logically split into some parts. So the first one is the simple API. This is a set of functions that will allow you to set get values, wait for events without dealing with any kind of resources. So this will do everything for you. And then there is the low level API where you have chip operations, line operations for event requests and then iterators that allow you to easily iterate over a set of lines. So these are some examples. So how does it look like in the low level API? This is not the simple API. This is the low level API where all the kernel structures, kernel UAPI structures are hidden behind opaque pointers. And I believe that it's much more straightforward to just open the chip, then get a line by index or by name. This is not shown here. Then specify if you want GPO line input. Actually this function has changed, but now it's a request line input. Request a line and then set and get value. So I encourage you to look because as I said this is the low level library API. If you look into the simple API you're going to see how exactly it is used. Unfortunately since I don't have my laptop I can't show you where to go. But yeah this is explained quite well in the simple API. So the tools. So the project is called Leap GPIO but I should have probably named it GPIO tools because this is probably the most used part of the project. So you have a couple of user space programs that allow you to detect the chips that are in the system. So when you run it it iterates over all the chips that it finds and displays their name, their label and the number of lines associated with it. Then there's a second program called GPIO info. You pass it the number of the name of the chip that you want to inspect and it runs through all the lines. So it basically calls the info on all the lines and displays you the info, so the offset, the name of the line, if it's unknown it's unknown and then the user here it's not used so it would be filled with the consumer. Its direction and active state. So you pass it the name of the GPIO find which allows you to easily find the GPIO by name. So you pass it the name of the GPIO line and it tells you which chip and which offset this line is on. Next tool is GPIO get. This is how you can get values so if you only know the GPIO by name you can run GPIO find and this output pass to get the current value of this line. And so yeah but usually you would use it like this so you can pass several offsets after the chip argument to get several values at once. So this is an example of how you use GPIO set but about GPIO set this requires more details. A GPIO line in the kernel can be by default driven high or low. Doesn't have to the pin can be floating but if it's actively driven you can't just exit from GPIO set so you can't just close the file descriptor because all the state after closing the GPIO chip file descriptor all the state returns to default. So what you want to do here is sometimes you want the program to still run while you wait for a user input because if the line is actively driven high or low it's going to return to the default after you exit from GPIO set. So if the pins are floating you can just exit if they are not you have to specify either mode wait or there are more modes you can wait for a signal you can wait a specified amount of time or you can wait for user input. Then an interesting program is GPIO MON which allows you to monitor lines for events. So by default it monitors all events you can specify additional parameters that allow you to only watch rising edge or falling edge events. This is the default output so it's human readable but you can specify a custom format if you want your events to be parsable by another script or another program you can specify a specific non-human readable format and the output will be like this or whatever you specify it to. These all programs are quite extensively documented so I believe that there's no time to get into much detail. So the state is like this I want to release the 1.0 version soon. This for the future so not for this version I want to introduce a GPIO demon which will allow you to take control more detailed control of the GPIOs in your system with better permission handling etc. C++ bindings are in progress it's going to be quite simple it's just for people using other languages and Python bindings since I notice that quite a lot of GPIO software space is done in Python for robotics for intelligent home system so this is going to be added as well and potential bindings for other systems yeah I don't know Node.js but if you know it and you would like to write bindings I would gladly accept them I'm not really a Node.js programmer but if you want to try it it's hosted at kernel.org in Libs LibGPIOD releases are also on kernel.org I made it available for build root but it's also I know that someone packaged it for meta open embedded also some other people packaged it for Fedora and Arch Linux in fact Fedora in their bleeding edge release or however it's called already deprecates C++ yeah so it's packaged for Fedora and Arch Linux and yeah, contributions are welcome please use the mailing list and oh it's remix, okay so we have time for questions so yeah sorry what? it works just as regular user space poll you can break it with a signal no problem so does the microphone work? no can you repeat your question? could you move back to the slide where is event registration for line? in the kernel API? yeah it's here I think it's not clear that you actually create an interrupt right? GPIO as interrupt oh this is done in the kernel so it's not really relevant for user space right? yeah, but I mean there's no connection at IRQ simulation it means this actually creates an interrupt right and goes through IRQ cheaper sorry if I wasn't clear that this interrupt simulation is done in the kernel module for the GPIO testing but this is just something that happens behind the scenes to be able to simulate interrupt yeah, but what I mean this one when you register this event it becomes an interrupt and if you cat proc interrupt you'll see that that is not clear from the slide I didn't think that it's relevant for user space for cat proc interrupt relevant for debugging at least but the question, the main question about this flags for line one slide back maybe where you put slides flags, yeah, when you retrieve flags why the list of flags only limited for those like open source, open drain why ways pull bias, like pull up pull down and everything because it's not implemented in the kernel it's not exposed for the user space yet but I guess this is something that in the future can be added these flags I guess are quite extensible okay and really a slight comment the CTO API has no version so it's not scalable to improve, to the update extent actually Linus Valet was mentioning that we can add additional flags, no problem with that anyway the pull up pull down is part of the pin control system not GPIO, that's totally different somehow GPIOs still are using GPIOs and it's property of GPIOs not pin control before pitch control it's a legacy so hi thanks for the talk first one question with the old legacy CISIFS approach there were a whole lot of context switches which made it very slow and you couldn't really do bit banging in user space, I suppose this is one big advantage of this app if you have this crucial use case of bit banging from user space then you can do it with this framework with the eventing you can see at least in the API of the kernel they used U64 for timestamp where was that so that is not a struck time spec which is usually used for such interfaces how does it relate to struck time spec and do you have any idea why they didn't choose that for the interface I wasn't involved in this API so when I started being involved with this this was already carved in stone frankly I don't know what the reason was for this but in the library I use struck time spec so the question is how does it relate to struck time spec you can convert it to time spec by dividing it by what is it 1 billion and then modulowing it for the nanoseconds this is what I do in the library maybe some other questions maybe some other questions sorry again timestamp to which timer does it relate this is a very good question I will look it up definitely I can't tell you right now okay so I think we don't have any more time I'm gonna be here around for a couple more minutes and I have a plane to catch afterwards so I won't be here anymore during the conference so thank you very much for coming