From: Bartosz Golaszewski Date: Mon, 25 Sep 2017 17:13:48 +0000 (+0200) Subject: simple: rework the event loop X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=a7ee495b12820d323ca4fca9c91e7403258d6d7c;p=qemu-gpiodev%2Flibgpiod.git simple: rework the event loop Implement a new routine for monitoring multiple lines with the simple event loop and extend the simple loop interface with custom polling capabilities. Signed-off-by: Bartosz Golaszewski --- diff --git a/include/gpiod.h b/include/gpiod.h index 6946a33..5d52eef 100644 --- a/include/gpiod.h +++ b/include/gpiod.h @@ -155,27 +155,86 @@ enum { * * The callback function takes the following arguments: event type (int), * GPIO line offset (unsigned int), event timestamp (const struct timespec *) - * and a pointer to user data. + * and a pointer to user data (void *). */ -typedef int (*gpiod_event_cb)(int, unsigned int, - const struct timespec *, void *); +typedef int (*gpiod_event_handle_cb)(int, unsigned int, + const struct timespec *, void *); + +/** + * @brief Return status values that the simple event poll callback can return. + */ +enum { + GPIOD_EVENT_POLL_ERR = -1, + /**< Polling error occurred (the polling function should set errno). */ + GPIOD_EVENT_POLL_TIMEOUT = 0, + /**< Poll timed out. */ + GPIOD_EVENT_POLL_EVENT = 1, + /**< Line event occurred. */ + GPIOD_EVENT_POLL_STOP = 2, + /**< The event loop should stop processing events. */ +}; + +/** + * @brief Simple event poll callback signature. + * + * The poll callback function takes the following arguments: number of lines + * (unsigned int), an array of file descriptors on which input events should + * be monitored (const int *), pointer to an integer which the function should + * set to the offset in the fd array corresponding with the descriptor on which + * an event occurred (int *), poll timeout (const struct timespec *) and a + * pointer to user data (void *). + * + * The callback should poll for input events on the set of descriptors and + * return an appropriate value that can be interpreted by the event loop + * routine. + */ +typedef int (*gpiod_event_poll_cb)(unsigned int, const int *, unsigned int *, + const struct timespec *, void *); /** * @brief Wait for events on a single GPIO line. * @param consumer Name of the consumer. * @param device Name, path or number of the gpiochip. - * @param offset GPIO line offset on the chip. + * @param offset GPIO line offset to monitor. * @param active_low The active state of this line - true if low. * @param timeout Maximum wait time for each iteration. - * @param callback Callback function to call on event occurence. - * @param cbdata User data passed to the callback. - * @return 0 no errors were encountered, -1 if an error occured. + * @param poll_cb Callback function to call when waiting for events. + * @param event_cb Callback function to call on event occurrence. + * @param data User data passed to the callback. + * @return 0 no errors were encountered, -1 if an error occurred. * + * The poll callback can be NULL in which case the routine will fall back to + * a basic, ppoll() based callback. */ int gpiod_simple_event_loop(const char *consumer, const char *device, unsigned int offset, bool active_low, const struct timespec *timeout, - gpiod_event_cb callback, void *cbdata) GPIOD_API; + gpiod_event_poll_cb poll_cb, + gpiod_event_handle_cb event_cb, + void *data) GPIOD_API; + +/** + * @brief Wait for events on multiple GPIO lines. + * @param consumer Name of the consumer. + * @param device Name, path or number of the gpiochip. + * @param offsets Array of GPIO line offsets to monitor. + * @param num_lines Number of lines to monitor. + * @param active_low The active state of this line - true if low. + * @param timeout Maximum wait time for each iteration. + * @param poll_cb Callback function to call when waiting for events. + * @param event_cb Callback function to call on event occurrence. + * @param data User data passed to the callback. + * @return 0 no errors were encountered, -1 if an error occurred. + * + * The callback functions work just like in the single line variant. + */ +int gpiod_simple_event_loop_multiple(const char *consumer, const char *device, + const unsigned int *offsets, + unsigned int num_lines, bool active_low, + const struct timespec *timeout, + gpiod_event_poll_cb poll_cb, + gpiod_event_handle_cb event_cb, + void *data) GPIOD_API; /** * @} diff --git a/src/lib/simple.c b/src/lib/simple.c index c2905c5..f7041d5 100644 --- a/src/lib/simple.c +++ b/src/lib/simple.c @@ -14,6 +14,9 @@ #include #include +#include + +#define UNUSED __attribute__((unused)) int gpiod_simple_get_value(const char *consumer, const char *device, unsigned int offset, bool active_low) @@ -134,57 +137,130 @@ int gpiod_simple_set_value_multiple(const char *consumer, const char *device, return 0; } +static int basic_event_poll(unsigned int num_lines, const int *fds, + unsigned int *event_offset, + const struct timespec *timeout, + void *data UNUSED) +{ + struct pollfd poll_fds[GPIOD_REQUEST_MAX_LINES]; + unsigned int i; + int status; + + memset(poll_fds, 0, sizeof(poll_fds)); + + for (i = 0; i < num_lines; i++) { + poll_fds[i].fd = fds[i]; + poll_fds[i].events = POLLIN | POLLPRI; + } + + status = ppoll(poll_fds, num_lines, timeout, NULL); + if (status < 0) { + if (errno == EINTR) + return GPIOD_EVENT_POLL_TIMEOUT; + else + return GPIOD_EVENT_POLL_ERR; + } else if (status == 0) { + return GPIOD_EVENT_POLL_TIMEOUT; + } + + for (i = 0; !poll_fds[i].revents; i++); + *event_offset = i; + + return GPIOD_EVENT_POLL_EVENT; +} + int gpiod_simple_event_loop(const char *consumer, const char *device, unsigned int offset, bool active_low, const struct timespec *timeout, - gpiod_event_cb callback, void *cbdata) + gpiod_event_poll_cb poll_cb, + gpiod_event_handle_cb event_cb, + void *data) +{ + return gpiod_simple_event_loop_multiple(consumer, device, &offset, 1, + active_low, timeout, poll_cb, + event_cb, data); +} + +int gpiod_simple_event_loop_multiple(const char *consumer, const char *device, + const unsigned int *offsets, + unsigned int num_lines, bool active_low, + const struct timespec *timeout, + gpiod_event_poll_cb poll_cb, + gpiod_event_handle_cb event_cb, + void *data) { + unsigned int i, event_offset, line_offset; + int fds[GPIOD_REQUEST_MAX_LINES]; struct gpiod_line_event event; - int status, evtype, flags; + struct gpiod_line_bulk bulk; struct gpiod_chip *chip; struct gpiod_line *line; + int ret, flags, evtype; + + if (num_lines > GPIOD_REQUEST_MAX_LINES) { + errno = EINVAL; + return -1; + } + + if (!poll_cb) + poll_cb = basic_event_poll; chip = gpiod_chip_open_lookup(device); if (!chip) return -1; - line = gpiod_chip_get_line(chip, offset); - if (!line) { - gpiod_chip_close(chip); - return -1; + gpiod_line_bulk_init(&bulk); + + for (i = 0; i < num_lines; i++) { + line = gpiod_chip_get_line(chip, offsets[i]); + if (!line) { + gpiod_chip_close(chip); + return -1; + } + + gpiod_line_bulk_add(&bulk, line); } flags = active_low ? GPIOD_REQUEST_ACTIVE_LOW : 0; - status = gpiod_line_request_both_edges_events_flags(line, consumer, - flags); - if (status < 0) { + ret = gpiod_line_request_bulk_both_edges_events_flags(&bulk, + consumer, flags); + if (ret) { gpiod_chip_close(chip); - return -1; + return ret; } + memset(fds, 0, sizeof(fds)); + for (i = 0; i < num_lines; i++) + fds[i] = gpiod_line_event_get_fd( + gpiod_line_bulk_get_line(&bulk, i)); + for (;;) { - status = gpiod_line_event_wait(line, timeout); - if (status < 0) { - if (errno == EINTR) - evtype = GPIOD_EVENT_CB_TIMEOUT; - else - goto out; - } else if (status == 0) { + ret = poll_cb(num_lines, fds, &event_offset, timeout, data); + if (ret < 0) { + goto out; + } else if (ret == GPIOD_EVENT_POLL_TIMEOUT) { evtype = GPIOD_EVENT_CB_TIMEOUT; + line_offset = 0; + } else if (ret == GPIOD_EVENT_POLL_STOP) { + ret = 0; + goto out; } else { - status = gpiod_line_event_read(line, &event); - if (status < 0) + line = gpiod_line_bulk_get_line(&bulk, event_offset); + ret = gpiod_line_event_read(line, &event); + if (ret < 0) goto out; evtype = event.event_type == GPIOD_EVENT_RISING_EDGE ? GPIOD_EVENT_CB_RISING_EDGE : GPIOD_EVENT_CB_FALLING_EDGE; + + line_offset = offsets[event_offset]; } - status = callback(evtype, offset, &event.ts, cbdata); - if (status == GPIOD_EVENT_CB_STOP) { - status = 0; + ret = event_cb(evtype, line_offset, &event.ts, data); + if (ret == GPIOD_EVENT_CB_STOP) { + ret = 0; goto out; } } @@ -192,5 +268,5 @@ int gpiod_simple_event_loop(const char *consumer, const char *device, out: gpiod_chip_close(chip); - return status; + return ret; } diff --git a/tests/tests-simple-api.c b/tests/tests-simple-api.c index ab89415..e9f38bf 100644 --- a/tests/tests-simple-api.c +++ b/tests/tests-simple-api.c @@ -152,7 +152,8 @@ static void simple_event_loop(void) test_set_event(0, 3, TEST_EVENT_ALTERNATING, 100); status = gpiod_simple_event_loop(TEST_CONSUMER, test_chip_name(0), 3, - false, &ts, simple_event_cb, &evdata); + false, &ts, NULL, simple_event_cb, + &evdata); TEST_ASSERT_RET_OK(status); TEST_ASSERT(evdata.got_rising_edge);