1

I want to use the GPIO pins to wait for a button-press without using a CPU spin loop. My preferred way of using the GPIO pins is via the sysfs interface at /sys/class/gpio, but it seems to me that there is an inherent race condition in doing so. Namely, if I understand the sysfs interface to GPIO correctly, it seems one must go through the following sequence:

  1. Read the value file to see whether the desired condition holds true.
  2. If it does not (the usual case for the first iteration), poll the value file for POLLPRI to sleep until it changes state, and repeat from step 1.

However, in this procedure, there is a window of opportunity between steps 1 and 2 such that the button is not yet pressed when the value is read, but then pushed right before entering doing the poll call, in which case this particular button press would effectively be missed.

I mean, I realize that a low-frequency event like button presses doesn't really have a high probability of triggering this race condition, but there are certainly more high-frequency events that could, and even regardless of that, it just seems ugly. Is there a way to avoid this problem?

Dolda2000
  • 111
  • 1
  • 4

2 Answers2

3

You should probably think about migrating to the new gpiochip interface for any new software. One improvement is that it gives you the likely time of GPIO level changes.

I have done pretty much what you describe with sysfs without noticing a problem. I suspect that once you have set the GPIO as an input and given an edge the system is primed to respond to level changes. That may be why I consume any prior interrupt in this code.

I suggest having a look in wiringPi or (my) pigpio which both have sysfs interrupt code.

/* 2014-07-06
   wfi.c

   gcc -o wfi wfi.c

   ./wfi [gpio]
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <poll.h>

#define GPIO 4

int main(int argc, char *argv[])
{
   char str[256];
   struct timeval tv;
   struct pollfd pfd;
   int fd, gpio;
   char buf[8];

   /*
      Prior calls assumed.
      sudo sh -c "echo 4      >/sys/class/gpio/export"
      sudo sh -c "echo in     >/sys/class/gpio/gpio4/direction"
      sudo sh -c "echo rising >/sys/class/gpio/gpio4/edge"
   */

   if (argc > 1) gpio = atoi(argv[1]);
   else          gpio = GPIO;

   sprintf(str, "/sys/class/gpio/gpio%d/value", gpio);

   if ((fd = open(str, O_RDONLY)) < 0)
   {
      fprintf(stderr, "Failed, gpio %d not exported.\n", gpio);
      exit(1);
   }

   pfd.fd = fd;

   pfd.events = POLLPRI;

   lseek(fd, 0, SEEK_SET);    /* consume any prior interrupt */
   read(fd, buf, sizeof buf);

   poll(&pfd, 1, -1);         /* wait for interrupt */

   lseek(fd, 0, SEEK_SET);    /* consume interrupt */
   read(fd, buf, sizeof buf);

   exit(0);
}

I didn't set up the edge and direction within the above code because I wanted the program to work without running with root permissions.

joan
  • 71,852
  • 5
  • 76
  • 108
1

Yes, there is a simple bash way for GPIO interrupt controlled read via sysfs interface:

echo 4 > /sys/class/gpio/export
echo in > /sys/class/gpio/gpio4/direction
echo both > /sys/class/gpio/gpio4/edge

and now the blocking wait for change interrupt for the resp. value (inotifywait will block until the value will change, e.g. falling or raising, but this will work only, if the edge has been set).

inotifywait -e modify /sys/class/gpio/gpio4/value
# here you may start your handling for a change of value

you may change the edge from both to falling or raising only, whatever you want. You can write a blocking watchdog script for every GPIO you want to watch with the lines above in an endless loop. Have fun. The gpio setting part above must be run by root, but only once at startup or any other suitable place. The inotify loop below could be run by any user.

gbslack
  • 11
  • 2