With Linux, some of the things that seem like they should be easy are not — at least at first glance. For example, how do you read an interrupt driven GPIO input in a Linux application? With simpler microcontroller systems, this is straightforward, but with a system like Linux, you have to navigate through several layers of software (and for very good reasons). You can’t handle interrupts directly in a Linux application, so this means you need a kernel component involved. This operation of reading a GPIO resembles a key press, so the Linux input subsystem might be a good place to start looking. Once we take that route, we discover the gpio_keys driver.
Configuring the gpio_keys driver
The gpio_keys driver is configured with a few lines of code in your Linux board configuration file. An example is below:
static struct gpio_keys_button svs_button_table[] = { { .code = KEY_RECORD, .gpio = PP_GPIO_MIC_EN, .active_low = 1, .desc = "MIC_EN", .type = EV_KEY, .wakeup = 0, }, };
In this application, we are reading button presses on a cell phone style headset. Once the gpio_keys driver is configured, a new entry will show up in /dev/input/eventX. An application can then do a blocking read on this device.
Reading the GPIO in an application
To read the GPIO, we simply do a blocking read on the new /dev/input/eventX device. The read will block until there is a change in GPIO state. An example is show below:
#define MIC_INPUT_DEV "/dev/input/event0" static gboolean mic_button_callback(GIOChannel *source, GIOCondition condition, gpointer data) { struct input_event ev; int bytes_read; g_io_channel_read_chars(source, (gchar *)&ev, sizeof(ev), &bytes_read, NULL); if (bytes_read > 0) { if (bytes_read != sizeof(ev)) { s_debug(1, "warning, only read %i bytes from mic input"); return TRUE; } } else { return TRUE; } if (ev.type != EV_SYN && ev.value == 1) { /* button pressed, do something ... */ } return TRUE; } void mic_button_init() { GIOChannel * micbutton = g_io_channel_new_file(MIC_INPUT_DEV, "r", NULL); if (micbutton == NULL) { s_debug(TRUE, "Error initializing mic button"); return; } g_io_channel_set_encoding(micbutton, NULL, NULL); guint id = g_io_add_watch(micbutton, G_IO_IN, mic_button_callback, NULL); }
The above example also shows how to incorporate the GPIO read into a GLib mainloop so that you don’t need to create a separate thread. ( As a side, GLib mainloop programming is worth learning!) Using this method, reading a GPIO interrupt is easy and requires very few lines of code. This is typical of complex systems like Linux — if you know how to do something, it is relatively easy, but getting started down the right path is sometimes the challenge.
I try this code by capturing /dev/input/event1 with the below mentioned code by for some reason i havent been able to figure out i end up getting “Error initializing mic button” . Any clues why this might be happening ?
#include
#include
#include
#include
#include
#include
#include
#include
#define MIC_INPUT_DEV “/dev/input/event1”
static gboolean mic_button_callback(GIOChannel *source, GIOCondition condition, gpointer data)
{
struct input_event ev;
int bytes_read;
g_io_channel_read_chars(source, (gchar *)&ev, sizeof(ev), &bytes_read, NULL);
if (bytes_read > 0) {
if (bytes_read != sizeof(ev)) {
// s_debug(1, “warning, only read %i bytes from mic input”);
printf(“warning, only read %i bytes from mic input”);
return TRUE;
}
} else {
return TRUE;
}
if (ev.type != EV_SYN && ev.value == 1) {
/* button pressed, do something … */
printf(“Power Key pressed YIPPY YIPPY”);
}
return TRUE;
}
void mic_button_init()
//void main()
{
GIOChannel * micbutton = g_io_channel_new_file(MIC_INPUT_DEV, “r”, NULL);
if (micbutton == NULL) {
// s_debug(TRUE, “Error initializing mic button”);
printf(“Error initializing mic button”);
return;
}
g_io_channel_set_encoding(micbutton, NULL, NULL);
guint id = g_io_add_watch(micbutton, G_IO_IN, mic_button_callback, NULL);
}
void main()
{
mic_button_init();
}
So the GIOChannel * micbutton = g_io_channel_new_file(MIC_INPUT_DEV, “r”, NULL); call must be failing. You might want to verify that the /dev/input/event1 device does exist, and perhaps test it first using one of the tools described in the following article: http://bec-systems.com/site/209/linux-input-testing-and-debugging.
Had to do a sudo inorder for the application to have sufficient permissions to access the event node. Thanks Cliff !
Can you please tell me how to cross compile that application. What are the header files and where can i find them. I am using openembedded to build kernel, on ubuntu. Thank you
You can set up a script similar to: http://cgit.bec-systems.com/cgit.cgi/oe-build/tree/scripts/setup-env-armv7-cross to use the OE cross compiler outside of OE.
cliff thanks for nice post.
Can you please tell me, if device is present in sys-fs instead of /dev/input then also callbacks are valid.
I mean
#define MIC_INPUT_DEV “/sys/devices/platfrom/pio0.4”
Thanks,
Hi,
I’m using this technique to trap a change on a GPIO line. The event is working and I do receive it when a change happens on the gpio. The thing is, I would like to be able to read the value of that GPIO when my program start. If it’s in a certain state, I would like to react right away. When I try to export the pin with echo > “xxx” /sys/class/gpio/export it isn’t created. Is it a limitation where I can’t use a gpio that is being used as an interrupt source at the same time as a gpio that I can poll?
Thanks!
Pingback: Beaglebot – A BeagleBoard based robot | Yet Another Hacker's Blog
Comments are closed.