1

I'm trying to catch an interrupt on GPIO through sysfs using poll(). I have -1 in the third position so it can block, but it seems to be always returning. I've checked out some similar posts on SO. Notably this (1), this (2), and this (3).

In (1), it was solved by placing a dummy read() before calling poll(). If I do this (see commented read() in code). My code runs through the loop once and blocks forever on poll() the second time around.

In (2), this might be an explanation, yet doesn't really provide a solution to my problem.

In (3), I already have an lseek() before my read()

How can I get this poll() to block and only return as in interrupt when the the gpio's value has changed?

Here's the code snippet:

int read_gpio(char *path, void (*callback)(int)){
    int fd;
    char buf[11];
    int res = 0;
    char c;
    int off;
    struct pollfd gpio_poll_fd = {
        .fd = fd,
        .events = POLLPRI,
        .revents = 0
    };
    for(;;){

        gpio_poll_fd.fd = open(path, O_RDONLY);
        if(fd == -1){
            perror("error opening file");
            return -1;
        }
//        char c;
//        read(fd,&c,1);
        LOGD("for begins");
        res = poll(&gpio_poll_fd,1,-1);
        LOGD("polling ended");
        if(res == -1){
            perror("error polling");
            break;
        }

        if((gpio_poll_fd.revents & POLLPRI)  == POLLPRI){
            LOGD("POLLPRI");
            off = lseek(fd, 0, SEEK_SET);
            if(off == -1) break;
            memset(&buf[0], 0, 11);
            size_t num = read(fd, &buf[0], 10*sizeof(char));
            LOGD("Before callback");
            callback(atoi(buf));
            LOGD("After Callback");
        }
        if((gpio_poll_fd.revents & POLLERR) == POLLERR) {
            //seems always to be true ..
            //LOGD("POLLERR");
        }
        close(fd);
        LOGD("for ends");

    }
    LOGD("for exits");

    return 0;
}

Note: As I'm doing this on Android JNI, I've been getting info for debugging from LOGD()

Update: Following the advice in jxh's comment I've arranged the structure like so, although now it blocks on poll() indefinitely. When the content of value is changed from the externally applied voltage, POLLPRI doesn't go high, and poll() doesn't return:

int read_gpio(char *path, void (*callback)(int)){
    int fd = open(path, O_RDONLY);
    if(fd == -1){
        perror("error opening file");
        return -1;
    }
    char buf[11];
    int res, off;
    char c;
    struct pollfd pfd = {
            .fd = fd,
            .events = POLLPRI,
            .revents = 0
    };
    for(;;){
        LOGD("for begins");
//        dummy read causes poll never to run
//        lseek() alone here cause poll never to run
//        read(fd, &buf[],1);
//        lseek(fd, 0, SEEK_SET);
        res = poll(&pfd,1,-1);
        LOGD("polling ended");
        if(res == -1){
            perror("error polling");
            break;
        }

        if((pfd.revents & POLLPRI)  == POLLPRI){
            LOGD("POLLPRI");
            off = lseek(fd, 0, SEEK_SET);
            if(off == -1) break;
            memset(&buf[0], 0, 11);
            read(fd, &buf[0], 10*sizeof(char));
//            These two lines will cause it to poll constantly
//            close(fd);
//            fd = open(path, O_RDONLY);
            LOGD("Before callback");
            callback(atoi(buf));
            LOGD("After Callback");
        }
        LOGD("for ends");
    }
    close(fd);
    LOGD("for exits");

    return 0;
}
Community
  • 1
  • 1
Stephan GM
  • 245
  • 3
  • 15
  • 1
    Are certain your `open()` succeeds? Your code that seems intended to test that is looking at the wrong variable. – John Bollinger Jun 03 '16 at 17:58
  • @JohnBollinger Definitely, I get the right value. Thanks for the heads up on the test. – Stephan GM Jun 03 '16 at 17:59
  • It's unclear why you suppose that the answer given in your reference (1) can be ignored. It seems to explain the behavior you report for the case presented, in which you do not read the file between opening it and polling it. Perhaps you really mean to ask why `poll()` blocks indefinitely when performed after that initial read? – John Bollinger Jun 03 '16 at 18:15
  • @JohnBollinger I'm not ignoring it, rather I tried to use their solution here. I tried to implement the solution that worked for them (see commented out `read()` for my placement of dummy read). So while (1) explains the behavior, the solution hasn't worked in my implementation. Maybe I have done something wrong, but I'm not sure what. – Stephan GM Jun 03 '16 at 18:19
  • Since the code works once when you use the dummy read, have you considered the possibility that that version of the code is ok? Are you certain that the file changes after the first pass without causing `poll()` to return? – John Bollinger Jun 03 '16 at 18:28
  • @JohnBollinger Yes, I'm sure because I'm changing the value with an external voltage supply and I can `cat value` from terminal and see that it has changed – Stephan GM Jun 03 '16 at 18:31
  • Is your edge attribute configured correctly? – n. m. could be an AI Jun 06 '16 at 17:15
  • @n.m. : Yes, configured to rising or falling. Tried w both – Stephan GM Jun 06 '16 at 17:20

1 Answers1

4

In your code, fd is not initialized.

When you open the file, you assign to gpio_poll_fd.fd directly, without using fd, so fd remains uninitialized.

Try:

gpio_poll_fd.fd = fd = open(path, O_RDONLY);

As pointed out in comments, according to the GPIO manual (which I had not read until after going through these comments more carefully), the GPIO sysfs interface is a little special:

If the pin can be configured as interrupt-generating interrupt and if it has been configured to generate interrupts (see the description of "edge"), you can poll(2) on that file and poll(2) will return whenever the interrupt was triggered. If you use poll(2), set the events POLLPRI and POLLERR. If you use select(2), set the file descriptor in exceptfds. After poll(2) returns, either lseek(2) to the beginning of the sysfs file and read the new value or close the file and re-open it to read the value.

So, although it is not the typical poll() idiom, your construct of closing and re-opening is correct. However, I would choose to leave the file descriptor open. So, here is how I would structure your code:

int read_gpio(char *path, void (*callback)(int)){
    char buf[11];
    int fd, res, off;
    struct pollfd pfd;
    if((pfd.fd = fd = open(path, O_RDONLY)) == -1){
        perror("path");
        return -1;
    }
    LOGD("First read");
    res = read(fd, buf, 10);
    assert(res == 10);
    LOGD("Before callback");
    callback(atoi(buf));
    LOGD("After Callback");
    pfd.events = POLLPRI|POLLERR;  // poll(2) says setting POLLERR is
                                   // unnecessary, but GPIO may be
                                   // special.
    for(;;){
        LOGD("for begins");
        if((res = poll(&pfd,1,-1)) == -1){
            perror("poll");
            break;
        }
        LOGD("polling ended");
        if((pfd.revents & POLLPRI) == POLLPRI){
            LOGD("POLLPRI");
            off = lseek(fd, 0, SEEK_SET);
            if(off == -1) break;
            memset(buf, 0, 11);
            res = read(fd, buf, 10);
            assert(res == 10);
            LOGD("Before callback");
            callback(atoi(buf));
            LOGD("After Callback");
        } else {
            // POLLERR, POLLHUP, or POLLNVAL
            break;
        }
        LOGD("for ends");
    }
    close(fd);
    LOGD("for exits");

    return 0;
}
jxh
  • 69,070
  • 8
  • 110
  • 193
  • I did this in the for `for(;;)`. Should it be done outside? – Stephan GM Jun 03 '16 at 18:37
  • Since you `close(fd)` on every iteration, inside the `for`. – jxh Jun 03 '16 at 18:40
  • Yes, this explains the problem completely. The sysfs FD is the one polled, but pretty much everywhere else it is the uninitialized `fd` that is manipulated. – John Bollinger Jun 03 '16 at 18:46
  • I replaced it `gpio_poll_fd.fd = open(path, O_RDONLY);` with `gpio_poll_fd.fd = fd = open(path, O_RDONLY);` as the first line of the `for` and it behaves exactly as it did before. Is this where I should have placed it? – Stephan GM Jun 03 '16 at 20:00
  • That placement should give you an initialized `fd` for use in `lseek` and `read`. – David C. Rankin Jun 03 '16 at 20:41
  • @DavidC.Rankin I agree it makes sense but it doesn't seem to solve the problem. Even more curious is how I could read the value from an 'uninitialized' `fd` before with `size_t num = read(fd, &buf[0], 10*sizeof(char));` and get the right value – Stephan GM Jun 03 '16 at 20:45
  • @jxh I change my structure as you described and it blocks forever. I also tried setting fd to nonblocking mode using `int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK);` but it hasn't seemed to change anything. I posted the code in the update – Stephan GM Jun 06 '16 at 16:34
  • @jxh it blocks on `poll()`. I had this problem originally and that was why I had the `open()` and `close()` at the beginning and end of the for loop, because they fixed `poll()` blocking indefinitely. – Stephan GM Jun 06 '16 at 16:49
  • @jxh TFM says "either lseek(2) to the beginning of the sysfs file and read the new value or close the file and re-open it to read the value." – n. m. could be an AI Jun 06 '16 at 17:08
  • @jxh It is already there, see the bottom of the loop. It makes it to the top again to hang on `poll()` so it passes through this `lseek()' – Stephan GM Jun 06 '16 at 18:42
  • @jxh `lseek(fd, 0, SEEK_SET); LOGD("For Ends");` this is after the read and before the `poll()` in the next loop iteration. Is that not the same thing? – Stephan GM Jun 06 '16 at 18:52
  • @ jxh Now it never polls and never reads. For the record I removed the one at the bottom of the loop. I've added some comments in the update to clarify what has been tried. – Stephan GM Jun 06 '16 at 19:01
  • @jxh Unfortunately, this code has the exact same behavior as the one in the update. It hangs on `poll()` after the first iteration. Also, for some reason "It simply always sets POLLERR for anything in sysfs" (see http://stackoverflow.com/questions/27411013/poll-returns-both-pollpri-pollerr). That's why I had the comment in `if((gpio_poll_fd.revents & POLLERR) == POLLERR)` in the original question. – Stephan GM Jun 06 '16 at 20:38
  • @StephanGM: How do you know it shouldn't hang? What proof do you have that it should wake up? – jxh Jun 06 '16 at 21:57
  • @jxh I `cat value` from `adb shell` and can see the `value` change as I apply voltage from my power supply – Stephan GM Jun 06 '16 at 22:02
  • @StephanGM: How did you verify you are polling the right file? – jxh Jun 06 '16 at 22:11
  • @jxh: When the content of `value` is changed from the externally applied voltage, `POLLPRI` doesn't go high, that's why `poll()` doesn't return. – Stephan GM Jun 07 '16 at 16:41
  • What is your trigger value for the `edge` file? What `gpio` command did you use? – jxh Jun 07 '16 at 18:51
  • @jxh: The `edge` can be either `rising`, `falling`, or `both` but it makes no difference, the result is the same. What do you mean by GPIO command? You mean what I used to set it? I used `echo rising > edge`. And if you were wondering, I'm sure the edge value is set as I check with `cat edge`. – Stephan GM Jun 07 '16 at 21:36
  • @jxh No its android on a freescale(NXP) WaRP board – Stephan GM Jun 08 '16 at 16:01
  • @jxh I tried this on another platform and the code worked. Therefore, I believe this is an issue w `sysfs` on my device and not the code. I upvoted your answer though as pointing out the direct assignment was helpful and flagged this for deletion. I believe if there is 3 or more upvotes you keep the points on deleted items. – Stephan GM Jun 16 '16 at 17:47