5

I'm using C to read the GPIO pins from /sys/class/gpio. I have a separate thread that I'd like to get notified when a pin changes value, so I'm opening the file for read, then calling poll() on it, hoping to only return when its value has changed. Apparently, however, I'm doing something wrong.

int fd = open(file, O_RDONLY);

// ...

struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;

int pollRet = poll(&pfd, 1, 100);
if (pollRet > 0) {
    if (pfd.revents & POLLIN) {
        int value = readFileValue(fd);
        printf("Value is now %d\n", value);
    }
}

// ...

int readFileValue(int fd) {
    lseek(fd, 0, SEEK_SET);
    char buffer[32] = {0};
    size_t br = read(fd, buffer, 32);
    return atoi(buffer);
}

This ends up reading the current value (0 or 1) from the file (in my case /sys/class/gpio/gpio21/value) immediately every time, even if it hasn't changed.

Is POLLIN the wrong flag to be checking for? Is there something I'm supposed to be doing to tell it, "yes, I've read that now" or something? I'm not really sure.

Ideas?

2

Is POLLIN the wrong flag to be checking for?

Yes, it's the wrong flag. Those "files" will always have something to read, as you notice.

As mentioned in the documentation, what you are looking for here isn't POLLIN -- it's

 POLLPRI | POLLERR

Or (same thing) POLLPRI + POLLERR.

So if you change this:

pfd.events = POLLIN;
[...]
if (pfd.revents & POLLIN) {

Appropriately you should get what you want. As also mentioned in the docs, you have to close/reopen or rewind the handle after each cycle, but you seem to be aware of that.

You might want to just define it first for simplicity:

#define POLL_GPIO = POLLPRI | POLLERR
  • Perfect! Thanks. I hadn't been able to come across that documentation in my Google searching. – tyrel Mar 21 '16 at 0:30
  • There's a lot of documentation like that in the kernel source, and if you work your way back up the path in that URL you'll find all of it. – goldilocks Mar 21 '16 at 0:32
4

Here is some code which waits for a GPIO to change state. It should do pretty much the same as you want to do.

/* 2014-07-06
   wfi.c
   Public Domain

   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);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.