poll() не блокируется, немедленно возвращается

Я пытаюсь поймать прерывание на GPIO через sysfs с помощью poll(), У меня -1 в третьей позиции, поэтому он может блокировать, но, кажется, всегда возвращается. Я проверил несколько подобных сообщений на SO. Особенно это (1), это (2), и это (3).

В (1) это было решено путем размещения манекена read() перед звонком poll(), Если я сделаю это (см. Комментарий read() в коде). Мой код проходит цикл один раз и навсегда блокируется poll() во второй раз.

В (2) это может быть объяснением, но не дает решения моей проблемы.

В (3) у меня уже есть lseek() до моего read()

Как я могу получить это poll() блокировать и возвращать только в случае прерывания, когда GPIO value изменилось?

Вот фрагмент кода:

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

Примечание: поскольку я делаю это на Android JNI, я получаю информацию для отладки от LOGD()

Обновление: следуя совету в комментарии jxh, я организовал структуру так, хотя теперь она блокируется poll() на неопределенный срок. Когда содержание значения изменяется от приложенного извне напряжения, POLLPRI не повышается, а poll() не возвращает:

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

1 ответ

В вашем коде fd не инициализируется.

Когда вы открываете файл, вы назначаете gpio_poll_fd.fd напрямую, без использования fd, так fd остается неинициализированным.

Пытаться:

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

Как указано в комментариях, согласно руководству по GPIO (которое я не читал до более тщательного изучения этих комментариев), GPIO sysfs интерфейс немного особенный:

Если вывод может быть сконфигурирован как прерывание, генерирующее прерывания, и если он был настроен для генерирования прерываний (см. Описание "фронта"), вы можете poll(2) в этом файле и poll(2) будет возвращаться всякий раз, когда прерывание было инициировано. Если вы используете poll(2)установить события POLLPRI а также POLLERR, Если вы используете select(2)установите дескриптор файла в exceptfds, После poll(2) возвращается, либо lseek(2) к началу sysfs файл и прочитайте новое значение или закройте файл и снова откройте его, чтобы прочитать значение.

Итак, хотя это не типичный poll() идиома, ваша конструкция закрытия и повторного открытия верна. Однако я бы предпочел оставить дескриптор файла открытым. Итак, вот как я бы структурировал ваш код:

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;
}
Другие вопросы по тегам