Как сгенерировать сигнал () для события GPIO в Linux - PullRequest
0 голосов
/ 11 октября 2019

Я хочу использовать Linux GPIO API для получения сигнала при изменении уровня GPIO.

Поскольку poll() получит событие POLLPRI, я предположил, что могу просто зарегистрировать обработчик дляSIGURG и используйте fcntl(), чтобы заставить fd, связанный с GPIO, отправлять сигнал при возникновении события.

На Raspberry PI с подключенными контактами GPIO 13 и 26 следующий код должен напечатать текущий уровеньс последующим «повышением» или «падением» каждую секунду:

#include <fcntl.h>
#include <linux/gpio.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define PIN_IN  (13)
#define PIN_OUT (26)

static int fd_in; /* to be read in the signal handler */

static int setup_pin_int(int fd, int pin, int mode, int flank) {
    struct gpioevent_request req = {
        .lineoffset  = pin,
        .handleflags = mode,
        .eventflags  = flank
    };

    int res = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &req);

    if (res < 0) {
        return res;
    }

    return req.fd;
}

static int setup_pin_out(int fd, int pin, int mode) {
    struct gpiohandle_request req = {
        .lineoffsets[0] = pin,
        .flags          = mode,
        .lines          = 1
    };

    int res = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);

    if (res < 0) {
        return res;
    }

    return req.fd;
}

static void pin_set(int fd, uint8_t val) {
    ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &val);
}

static void sigurg_handler(void) {
    struct gpioevent_data event;
    read(fd_in, &event, sizeof(event));

    if (event.id == GPIOEVENT_EVENT_RISING_EDGE) {
        puts("rising");
    }
    if (event.id == GPIOEVENT_EVENT_FALLING_EDGE) {
        puts("falling");
    }
}

static void signal_handler(int signal) {
    printf("got signal: %x\n", signal);

    if (signal == SIGURG) {
        sigurg_handler();
    }
}

static void register_signal(int signal, void (*handler)(int)) {
    struct sigaction sa = {
        .sa_handler = handler
    };

    sigaction(signal, &sa, NULL);
}

static void _do_poll(int fd) {

    struct pollfd pfd = {
        .fd = fd,
        .events = POLLIN | POLLPRI
    };

    if (poll(&pfd, 1, 1000) > 0) {
        sigurg_handler();
    }
}

int main(void) {

    int pins[2];

    int fd = open("/dev/gpiochip0", O_RDWR);

    pins[0] = setup_pin_int(fd, PIN_IN, GPIOHANDLE_REQUEST_INPUT, GPIOEVENT_REQUEST_BOTH_EDGES);
    pins[1] = setup_pin_out(fd, PIN_OUT, GPIOHANDLE_REQUEST_OUTPUT);

    fd_in = pins[0];

    /* register signal handler */
    register_signal(SIGIO, signal_handler);
    register_signal(SIGURG, signal_handler);

    /* make the fd generate signals */
    fcntl(fd_in, F_SETOWN, getpid());
    fcntl(fd_in, F_SETFL, O_NONBLOCK | O_ASYNC);

    /* toggle the output pin each second */
    int state;
    while (1) {
        state = !state;
        sleep(1);

        printf("set %d\n", state);
        pin_set(pins[1], state);

         /* poll() is working on the fd */
//        _do_poll(fd_in);
    }

    close(fd);

    return 0;
}

Однако, это происходит сейчас.

Я добавил функцию _do_poll(), которая вручную опрашивает GPIO fd, чтобы подтвердить, чтоPOLLPRI событие генерируется.

Но как мне превратить это в сигнал? Другим решением было бы fork() и использование poll() в дочернем элементе, посылая надлежащий сигнал родителю при возврате poll(), но я бы хотел избежать этих накладных расходов.

...