simple-api: handle receiving multiple events
authorBartosz Golaszewski <bartekgola@gmail.com>
Wed, 11 Oct 2017 13:37:32 +0000 (15:37 +0200)
committerBartosz Golaszewski <bartekgola@gmail.com>
Wed, 11 Oct 2017 14:47:12 +0000 (16:47 +0200)
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 <bartekgola@gmail.com>
include/gpiod.h
src/lib/simple.c
src/tools/gpiomon.c

index d1efd62d66d63fe351f26ddfa94121726f564ef0..7ef3ce436fa69eefea9e3afa9ffd4c8889eb3ed6 100644 (file)
@@ -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 *);
 
 /**
index 905e1d6613bf57f7d8fd4ffe9e8c9ff5c0c99d7c..04fcf49c1de0c59222ec5f80f7e18e4bf84ab352 100644 (file)
@@ -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;
                }
        }
 
index d54234dd4951b3d23e0d6db3dd1bed10fafa8ccd..838c5b22a6cf964416bba7dff37052e8dda1abe1 100644 (file)
@@ -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;
                }
        }