From 07698f995016aade4a5bbf8b9efafd6b8536e00e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 11 Oct 2017 15:37:32 +0200 Subject: [PATCH] simple-api: handle receiving multiple events Make the simple event loop poll callback capable of signalling to the caller that more than one event occurred. Make the event loop routine for multiple lines aware of that so that it reads all the event data queued up instead of needlessly polling in that case. Signed-off-by: Bartosz Golaszewski --- include/gpiod.h | 24 ++++++----- src/lib/simple.c | 98 ++++++++++++++++++++++++++++----------------- src/tools/gpiomon.c | 20 ++++----- 3 files changed, 88 insertions(+), 54 deletions(-) diff --git a/include/gpiod.h b/include/gpiod.h index d1efd62..7ef3ce4 100644 --- a/include/gpiod.h +++ b/include/gpiod.h @@ -170,14 +170,22 @@ typedef int (*gpiod_simple_event_handle_cb)(int, unsigned int, * @brief Return status values that the simple event poll callback can return. */ enum { + GPIOD_SIMPLE_EVENT_POLL_RET_STOP = -2, + /**< The event loop should stop processing events. */ GPIOD_SIMPLE_EVENT_POLL_RET_ERR = -1, /**< Polling error occurred (the polling function should set errno). */ GPIOD_SIMPLE_EVENT_POLL_RET_TIMEOUT = 0, /**< Poll timed out. */ - GPIOD_SIMPLE_EVENT_POLL_RET_EVENT = 1, - /**< Line event occurred. */ - GPIOD_SIMPLE_EVENT_POLL_RET_STOP = 2, - /**< The event loop should stop processing events. */ +}; + +/** + * @brief Helper structure for the simple event loop poll callback. + */ +struct gpiod_simple_event_poll_fd { + int fd; + /**< File descriptor number. */ + bool event; + /**< Indicates whether an event occurred on this file descriptor. */ }; /** @@ -185,17 +193,15 @@ enum { * * 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 *). + * be monitored (struct gpiod_simple_event_poll_fd *), 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_simple_event_poll_cb)(unsigned int, - const int *, unsigned int *, + struct gpiod_simple_event_poll_fd *, const struct timespec *, void *); /** diff --git a/src/lib/simple.c b/src/lib/simple.c index 905e1d6..04fcf49 100644 --- a/src/lib/simple.c +++ b/src/lib/simple.c @@ -137,36 +137,45 @@ 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, +static int basic_event_poll(unsigned int num_lines, + struct gpiod_simple_event_poll_fd *fds, const struct timespec *timeout, void *data GPIOD_UNUSED) { struct pollfd poll_fds[GPIOD_LINE_BULK_MAX_LINES]; unsigned int i; - int status; + int rv, ret; + + if (num_lines > GPIOD_LINE_BULK_MAX_LINES) + return GPIOD_SIMPLE_EVENT_POLL_RET_ERR; memset(poll_fds, 0, sizeof(poll_fds)); for (i = 0; i < num_lines; i++) { - poll_fds[i].fd = fds[i]; + poll_fds[i].fd = fds[i].fd; poll_fds[i].events = POLLIN | POLLPRI; } - status = ppoll(poll_fds, num_lines, timeout, NULL); - if (status < 0) { + rv = ppoll(poll_fds, num_lines, timeout, NULL); + if (rv < 0) { if (errno == EINTR) return GPIOD_SIMPLE_EVENT_POLL_RET_TIMEOUT; else return GPIOD_SIMPLE_EVENT_POLL_RET_ERR; - } else if (status == 0) { + } else if (rv == 0) { return GPIOD_SIMPLE_EVENT_POLL_RET_TIMEOUT; } - for (i = 0; !poll_fds[i].revents; i++); - *event_offset = i; + ret = rv; + for (i = 0; i < num_lines; i++) { + if (poll_fds[i].revents) { + fds[i].event = true; + if (!--rv) + break; + } + } - return GPIOD_SIMPLE_EVENT_POLL_RET_EVENT; + return ret; } int gpiod_simple_event_loop(const char *consumer, const char *device, @@ -189,13 +198,13 @@ int gpiod_simple_event_loop_multiple(const char *consumer, const char *device, gpiod_simple_event_handle_cb event_cb, void *data) { - unsigned int i, event_offset, line_offset; - int fds[GPIOD_LINE_BULK_MAX_LINES]; + struct gpiod_simple_event_poll_fd fds[GPIOD_LINE_BULK_MAX_LINES]; + int rv, ret, flags, evtype, cnt; struct gpiod_line_event event; struct gpiod_line_bulk bulk; struct gpiod_chip *chip; struct gpiod_line *line; - int ret, flags, evtype; + unsigned int i; if (num_lines > GPIOD_LINE_BULK_MAX_LINES) { errno = EINVAL; @@ -223,46 +232,63 @@ int gpiod_simple_event_loop_multiple(const char *consumer, const char *device, flags = active_low ? GPIOD_LINE_REQUEST_ACTIVE_LOW : 0; - ret = gpiod_line_request_bulk_both_edges_events_flags(&bulk, + rv = gpiod_line_request_bulk_both_edges_events_flags(&bulk, consumer, flags); - if (ret) { - gpiod_chip_close(chip); - return ret; + if (rv) { + ret = -1; + goto out; } 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 (i = 0; i < num_lines; i++) { + line = gpiod_line_bulk_get_line(&bulk, i); + fds[i].fd = gpiod_line_event_get_fd(line); + } for (;;) { - ret = poll_cb(num_lines, fds, &event_offset, timeout, data); - if (ret < 0) { + for (i = 0; i < num_lines; i++) + fds[i].event = false; + + cnt = poll_cb(num_lines, fds, timeout, data); + if (cnt == GPIOD_SIMPLE_EVENT_POLL_RET_ERR) { + ret = -1; goto out; - } else if (ret == GPIOD_SIMPLE_EVENT_POLL_RET_TIMEOUT) { - evtype = GPIOD_SIMPLE_EVENT_CB_TIMEOUT; - line_offset = 0; - } else if (ret == GPIOD_SIMPLE_EVENT_POLL_RET_STOP) { + } else if (cnt == GPIOD_SIMPLE_EVENT_POLL_RET_TIMEOUT) { + rv = event_cb(GPIOD_SIMPLE_EVENT_CB_TIMEOUT, + 0, &event.ts, data); + if (rv == GPIOD_SIMPLE_EVENT_CB_RET_STOP) { + ret = 0; + goto out; + } + } else if (cnt == GPIOD_SIMPLE_EVENT_POLL_RET_STOP) { ret = 0; goto out; - } else { - line = gpiod_line_bulk_get_line(&bulk, event_offset); - ret = gpiod_line_event_read(line, &event); - if (ret < 0) + } + + for (i = 0; i < num_lines; i++) { + if (!fds[i].event) + continue; + + line = gpiod_line_bulk_get_line(&bulk, i); + rv = gpiod_line_event_read(line, &event); + if (rv < 0) { + ret = rv; goto out; + } if (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE) evtype = GPIOD_SIMPLE_EVENT_CB_RISING_EDGE; else evtype = GPIOD_SIMPLE_EVENT_CB_FALLING_EDGE; - line_offset = offsets[event_offset]; - } + rv = event_cb(evtype, gpiod_line_offset(line), &event.ts, data); + if (rv == GPIOD_SIMPLE_EVENT_CB_RET_STOP) { + ret = 0; + goto out; + } - ret = event_cb(evtype, line_offset, &event.ts, data); - if (ret == GPIOD_SIMPLE_EVENT_CB_RET_STOP) { - ret = 0; - goto out; + if (!--cnt) + break; } } diff --git a/src/tools/gpiomon.c b/src/tools/gpiomon.c index d54234d..838c5b2 100644 --- a/src/tools/gpiomon.c +++ b/src/tools/gpiomon.c @@ -140,17 +140,17 @@ static void event_print_human_readable(unsigned int offset, evname, offset, ts->tv_sec, ts->tv_nsec); } -static int poll_callback(unsigned int num_lines, const int *fds, - unsigned int *event_offset, +static int poll_callback(unsigned int num_lines, + struct gpiod_simple_event_poll_fd *fds, const struct timespec *timeout, void *data) { struct pollfd pfds[GPIOD_LINE_BULK_MAX_LINES + 1]; struct mon_ctx *ctx = data; + int cnt, ts, ret; unsigned int i; - int ret, ts; for (i = 0; i < num_lines; i++) { - pfds[i].fd = fds[i]; + pfds[i].fd = fds[i].fd; pfds[i].events = POLLIN | POLLPRI; } @@ -159,16 +159,18 @@ static int poll_callback(unsigned int num_lines, const int *fds, ts = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000; - ret = poll(pfds, num_lines + 1, ts); - if (ret < 0) + cnt = poll(pfds, num_lines + 1, ts); + if (cnt < 0) return GPIOD_SIMPLE_EVENT_POLL_RET_ERR; - else if (ret == 0) + else if (cnt == 0) return GPIOD_SIMPLE_EVENT_POLL_RET_TIMEOUT; + ret = cnt; for (i = 0; i < num_lines; i++) { if (pfds[i].revents) { - *event_offset = i; - return GPIOD_SIMPLE_EVENT_POLL_RET_EVENT; + fds[i].event = true; + if (!--cnt) + return ret; } } -- 2.30.2