*
* 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;
/**
* @}
#include <string.h>
#include <errno.h>
+#include <poll.h>
+
+#define UNUSED __attribute__((unused))
int gpiod_simple_get_value(const char *consumer, const char *device,
unsigned int offset, bool active_low)
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;
}
}
out:
gpiod_chip_close(chip);
- return status;
+ return ret;
}