Reputation: 245
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;
}
Upvotes: 1
Views: 3660
Reputation: 70392
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 andpoll(2)
will return whenever the interrupt was triggered. If you usepoll(2)
, set the eventsPOLLPRI
andPOLLERR
. If you useselect(2)
, set the file descriptor inexceptfds
. Afterpoll(2)
returns, eitherlseek(2)
to the beginning of thesysfs
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;
}
Upvotes: 4