GPIO Event Driver
From Gumstix User Wiki
This page documents the gpio-event driver, which allows multiple GPIO lines to be monitored from user-space.
The gpio-event driver consists of a loadable kernel module, which registers an interrupt handler, along with an example user-mode program, which allows the settings to be manipulated and changes to be reported.
The source code can be found here. The app directory contains the example user mode application, and the module directory contains the loadable kernel module. The Makefiles have been setup such that if you define the environment variable OVEROTOP then you should be able to just type make in top level directory and both the app and module will be built.
Adding pins to be monitored
The gpio-event program can be used to add gpio pins to be monitored. Simply run the gpio-event program, passing it a pin specification of the form: pin:edge:debounce. pin should be the GPIO number that you want to monitor. Edge should be r, f, or b to monitor rising, falling, or both edges, and debounce should be the number of milliseconds of debounce that you'd like. A debounce of 0 may be used for "clean" signals. For example:
gpio-event 58:f:20 59:r:20 60:b:20
would cause gpio 58 to be monitored for falling edges, gpio 59 to be monitored for rising edges, and gpio 60 to be monitored for both rising and falling edges. Each pin will have a debounce of 20 milliseconds (which should be adequate for most pushbuttons).
You can pass as many pins to be monitored as you'd like on the command line. Each call is cumulative. If you specify the same pin, the new specification will override whatever is currently set. You can see the current pins being monitored by using:
Passing in a negative gpio number will cause the pin to stop being monitored.
Once pins are setup to be monitored, any program can read /dev/gpio-event to get notified of the events. By default, an ASCII string of the form: "pin edge timestamp" followed by a newline will be delievered to /dev/gpio-event for each event that occurs. Running gpio-event with the -m or --monitor command line will cause the events to be monitored and printed. You could also use the command:
to achieve a similar result. For C programs, you can opt to use a binary interface instead. The binary versus ASCII setting is maintained on a per-file basis, so each program could each open /dev/gpio-event with a different ASCII/binary setting.
The edge will be an R or an F depending on whether a rising or falling edge is being reported. The timestamp is a number with the integer portion being the current system time (as returned from the gettimeofday function) and the fractional portion reporting in microseconds.
Lauching a script
The gpio-event program can also be given the -e or --execute command line option, and the provided program or script will be launched with the same 3 paramters (gpio edge timestamp). For example:
gpio-event --execute echo
will produce similar output to
Integrating/Building with Open Embedded
In order to build the driver under OE, you can do the following:
cd to the directory containing org.embedded.dev svn co http://svn.hylands.org/linux/oe/user.collection user.collection bitbake task-gpio-all
This will build the following packages for gpio-event:
and these packages for the User GPIO Driver
./deploy/glibc/ipk/overo/gpio-module_svn20100218-r50.5_overo.ipk ./deploy/glibc/ipk/armv7a/gpio-app_svn20100218-r0.5_armv7a.ipk ./deploy/glibc/ipk/armv7a/gpio-lib_svn20100218-r0.5_armv7a.ipk
Note that the numeric portions of the pathnames will almost certainly be different.
How it works
Everything starts with the module_init directive near the very end of the gpio-event-drv.c file. The function passed to this directive, namely gpio_event_init, will be called automatically when the module is loaded. Similarly, the gpio_event_exit function, passed to the module_exit directive, will be called when the module is unloaded.
- alloc_chrdev_region - Allocates a major number dynamically
- create_proc_entry - Creates a directory, namely /proc/gpio-event
- create_proc_entry - Creates a proc entry, namely /proc/gpio-event/pins. This particular proc entry is processed using the functions described in pins_proc_ops.
- register_sysctl_table - Registers some sysctl entries, described by gSysCtl and gSysCtlSample. I find using sysctl entries a quick and easy way to enable debug flags in my drivers. The sysctl entries will appear in /proc/sys/gpio-event and cause the global variables gLostEvents, gDebugTrace, gDebugIoctl, and gDebugError to be manipulated.
- cdev_init associates the functions in gpio_event_fops against the major number allocated earlier with alloc_chrdev_region.
- cdev_add registers the driver with the kernel, and makes it "go live". Once cdev_add is called, user mode programs can open the driver.
- class_create creates a class, and device_create registers that class. This gets picked up by udev which will create the /dev/gpio-entry and associate with the dynamically allocated major number.
gpio_event_open is called when a user mode program calls open on /dev/gpio-event. The gpio-event driver assumes that it can be opened by multiple applications simultaneously, and uses a GPIO_FileData_t structure to keep track of each instance of the driver which is open. gFileList is a kernel list which contains a list of all of the currently open GPIO_FileData_t data structures.
GPIO_FileData_t data structure
|list||Element used to make list of GPIO_FileData_t structures|
|waitQueue||Object that reader waits on when no events are available.|
|queueLock||Mutual exclusion lock acquired when manipulating queueData, getIndex, putIndex, or numEvents.|
|queueData||Circular queue of events waiting to be read for a given file.|
|getIndex||Index of the next item to be retrieved from the circular queue.|
|putIndex||Index of the next place that an event will be placed into the circular queue.|
|numEvents||The number of events currently in the circular queue.|
|readMode||Determines if this file is being read in binary or ASCII mode. Defaults to ASCII.|
|buffer||Contains the next event worth of data, formatted in either binary or ASCII format.|
|bufBytes||The number of bytes of data that are currently sitting in buffer.|
Note: When manipuating the gFileList linked list, the driver should hold the gFileListLock spinlock with interrupts disabled.
gpio_event_ioctl is called when a user mode program issues an ioctl call on the file handle returned from opening /dev/gpio-event. The user mode program can use GPIO_EVENT_IOCTL_MONITOR_GPIO to add/removee/modify monitoring for a pin. The user mode program can use GPIO_EVENT_IOCTL_SET_READ_MODE to set the read mode to binary or ASCII.
gpio_event_poll adds the appropriate glue to allow the poll or select calls to work as expected from user mode. Basically, this function determines if there is data to be read or not.
gpio_event_read is called when the user issues a read call on the files handle returned from opening /dev/gpio-event. It first checks to see if any data remains from previous calls. If a non-blocking read is performed and no events are available, then it returns immediately, otherwise it blocks waiting for an event to be placed in the queue before returning. If multiple events are available, and the caller provided sufficient buffer space, then as much data as can be returned will be. gpio_event_dequeue_event is called to retrieve queued events.
gpio_event_release is called when a given file handle is closed. It removes the GPIO_FileData_t structure from the list of open files.
gpio_event_monitor is called in response to the GPIO_EVENT_IOCTL_MONITOR_GPIO ioctl. If monitoring for a pin is being added, then request_irq is called to register an interrupt handler, gpio_event_irq, whenever an appropriate type of edge is detected, and init_timer is called to setup the debounce timer. If monitoring for a pin is being removed, then free_irq is called, along with del_timer_sync to unregister the timer.
GPIO_PinData_t data structure
There is an instance of this structure for each pin which is actively being monitored.
|list||Element used to make list of GPIO_PinData_t structures|
|gpio||The gpio pin which is being monitored.|
|debounceTimer||Timer object used to perform debounce.|
|debounceMilliSec||Amount of time that the debounce timer should be activated for after an edge.|
|devName||device name used when registering an irq handler (shows up in /proc/irq)|
|edgeType||The type of edge that is being monitored.|
|pinState||Holds the current state of the pin.|
Note: When manipuating the gPinList linked list, the driver should hold the gPinListLock spinlock with interrupts disabled.
gpio_event_irq is called whenever an edge is detected on enabled pins. The type of edge is determined and an event is queued using gpio_event_queue_event. If pin deboucing is used, then interrupts are disabled (for the particular pin) and a timer is started.
If debouncing is used on a pin, then a timer is started when an edge occurs. This allows interrupts to be disabled for a short time when the pin "bounces" or generates lots of edges. At the end of the timeout, interrupts are re-enabled.
gpio_event_dequeue_event is called to retrieve any events queued up for a given file descriptor.
gpio_event_queue_event adds an event to all open file descriptors. The waitQueues for each file descriptor is also "woken" which allows any readers which are blocked waiting for an event to run.
find_pin is a utility function to detemine if a particular gpio is being monitored.
Called when a user mode program opens /proc/gpio-event/pins
Set of functions which are called to process the output which goes to /proc/gpio-event/pins.