diff options
author | Bartosz Golaszewski <bartekgola@gmail.com> | 2018-10-07 15:49:50 +0200 |
---|---|---|
committer | Bartosz Golaszewski <bartekgola@gmail.com> | 2018-10-07 15:49:50 +0200 |
commit | 4bf402d3a49336eacd33654441d575bd267780b8 (patch) | |
tree | c9fe11aa42acf3b46544584ff265ca94958d8394 | |
parent | 63c72c92d60702a6d9445c7b65482ad4eb7a0c06 (diff) | |
parent | 5095bdd01a62eeb6c5ca9a38b6c84272f653134c (diff) |
Merge branch 'next'
This introduces the new context-less event monitor with specifiable
event types and a small tweak in configure.ac.
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | include/gpiod.h | 84 | ||||
-rw-r--r-- | src/lib/ctxless.c | 58 | ||||
-rw-r--r-- | src/tools/gpiomon.c | 41 | ||||
-rw-r--r-- | tests/tests-ctxless.c | 77 |
5 files changed, 207 insertions, 55 deletions
diff --git a/configure.ac b/configure.ac index 445bd0c..782b395 100644 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ AC_SUBST(VERSION_STR, [$PACKAGE_VERSION$EXTRA_VERSION]) # # (...) # 3. If the library source code has changed at all since the last update, then -# increment revision (‘c:r:a’ becomes ‘c:r+1:a’). +# increment revision ('c:r:a' becomes 'c:r+1:a'). # 4. If any interfaces have been added, removed, or changed since the last # update, increment current, and set revision to 0. # 5. If any interfaces have been added since the last public release, then diff --git a/include/gpiod.h b/include/gpiod.h index 5ae45aa..ccff977 100644 --- a/include/gpiod.h +++ b/include/gpiod.h @@ -70,6 +70,11 @@ struct gpiod_line_bulk; #define GPIOD_BIT(nr) (1UL << (nr)) /** + * @brief Marks a public function as deprecated. + */ +#define GPIOD_DEPRECATED __attribute__((deprecated)) + +/** * @} * * @defgroup __high_level__ High-level API @@ -149,6 +154,18 @@ int gpiod_ctxless_set_value_multiple(const char *device, void *data) GPIOD_API; /** + * @brief Event types that the ctxless event monitor can wait for. + */ +enum { + /**< Wait for rising edge events only. */ + GPIOD_CTXLESS_EVENT_RISING_EDGE = 1, + /**< Wait for falling edge events only. */ + GPIOD_CTXLESS_EVENT_FALLING_EDGE, + /**< Wait for both types of events. */ + GPIOD_CTXLESS_EVENT_BOTH_EDGES, +}; + +/** * @brief Event types that can be passed to the ctxless event callback. */ enum { @@ -247,7 +264,7 @@ int gpiod_ctxless_event_loop(const char *device, unsigned int offset, const struct timespec *timeout, gpiod_ctxless_event_poll_cb poll_cb, gpiod_ctxless_event_handle_cb event_cb, - void *data) GPIOD_API; + void *data) GPIOD_API GPIOD_DEPRECATED; /** * @brief Wait for events on multiple GPIO lines. @@ -284,7 +301,70 @@ int gpiod_ctxless_event_loop_multiple(const char *device, const struct timespec *timeout, gpiod_ctxless_event_poll_cb poll_cb, gpiod_ctxless_event_handle_cb event_cb, - void *data) GPIOD_API; + void *data) GPIOD_API GPIOD_DEPRECATED; + +/** + * @brief Wait for events on a single GPIO line. + * @param device Name, path, number or label of the gpiochip. + * @param event_type Type of events to listen for. + * @param offset GPIO line offset to monitor. + * @param active_low The active state of this line - true if low. + * @param consumer Name of the consumer. + * @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 for each line event. + * @param data User data passed to the callback. + * @return 0 if no errors were encountered, -1 if an error occurred. + * @note The way the ctxless event loop works is described in detail in + * ::gpiod_ctxless_event_monitor_multiple - this is just a wrapper aound + * this routine which calls it for a single GPIO line. + */ +int gpiod_ctxless_event_monitor(const char *device, int event_type, + unsigned int offset, bool active_low, + const char *consumer, + const struct timespec *timeout, + gpiod_ctxless_event_poll_cb poll_cb, + gpiod_ctxless_event_handle_cb event_cb, + void *data) GPIOD_API; + +/** + * @brief Wait for events on multiple GPIO lines. + * @param device Name, path, number or label of the gpiochip. + * @param event_type Type of events to listen for. + * @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 consumer Name of the consumer. + * @param timeout Maximum wait time for each iteration. + * @param poll_cb Callback function to call when waiting for events. Can + * be NULL. + * @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. + * @note The poll callback can be NULL in which case the routine will fall + * back to a basic, ppoll() based callback. + * + * Internally this routine opens the GPIO chip, requests the set of lines for + * the type of events specified in the event_type paramter and calls the + * polling callback in a loop. The role of the polling callback is to detect + * input events on a set of file descriptors and notify the caller about the + * fds ready for reading. + * + * The ctxless event loop then reads each queued event from marked descriptors + * and calls the event callback. Both callbacks can stop the loop at any + * point. + * + * The poll_cb argument can be NULL in which case the function falls back to + * a default, ppoll() based callback. + */ +int gpiod_ctxless_event_monitor_multiple( + const char *device, int event_type, + const unsigned int *offsets, + unsigned int num_lines, bool active_low, + const char *consumer, const struct timespec *timeout, + gpiod_ctxless_event_poll_cb poll_cb, + gpiod_ctxless_event_handle_cb event_cb, + void *data) GPIOD_API; /** * @brief Determine the chip name and line offset of a line with given name. diff --git a/src/lib/ctxless.c b/src/lib/ctxless.c index f3545cf..0009504 100644 --- a/src/lib/ctxless.c +++ b/src/lib/ctxless.c @@ -181,9 +181,10 @@ int gpiod_ctxless_event_loop(const char *device, unsigned int offset, gpiod_ctxless_event_handle_cb event_cb, void *data) { - return gpiod_ctxless_event_loop_multiple(device, &offset, 1, - active_low, consumer, timeout, - poll_cb, event_cb, data); + return gpiod_ctxless_event_monitor(device, + GPIOD_CTXLESS_EVENT_BOTH_EDGES, + offset, active_low, consumer, + timeout, poll_cb, event_cb, data); } int gpiod_ctxless_event_loop_multiple(const char *device, @@ -195,10 +196,41 @@ int gpiod_ctxless_event_loop_multiple(const char *device, gpiod_ctxless_event_handle_cb event_cb, void *data) { + return gpiod_ctxless_event_monitor_multiple( + device, GPIOD_CTXLESS_EVENT_BOTH_EDGES, + offsets, num_lines, active_low, consumer, + timeout, poll_cb, event_cb, data); +} + +int gpiod_ctxless_event_monitor(const char *device, int event_type, + unsigned int offset, bool active_low, + const char *consumer, + const struct timespec *timeout, + gpiod_ctxless_event_poll_cb poll_cb, + gpiod_ctxless_event_handle_cb event_cb, + void *data) +{ + return gpiod_ctxless_event_monitor_multiple(device, event_type, + &offset, 1, active_low, + consumer, timeout, + poll_cb, event_cb, data); +} + +int gpiod_ctxless_event_monitor_multiple( + const char *device, int event_type, + const unsigned int *offsets, + unsigned int num_lines, bool active_low, + const char *consumer, + const struct timespec *timeout, + gpiod_ctxless_event_poll_cb poll_cb, + gpiod_ctxless_event_handle_cb event_cb, + void *data) +{ struct gpiod_ctxless_event_poll_fd fds[GPIOD_LINE_BULK_MAX_LINES]; - int rv, ret, flags, evtype, cnt; + struct gpiod_line_request_config conf; struct gpiod_line_event event; struct gpiod_line_bulk bulk; + int rv, ret, evtype, cnt; struct gpiod_chip *chip; struct gpiod_line *line; unsigned int i; @@ -227,10 +259,22 @@ int gpiod_ctxless_event_loop_multiple(const char *device, gpiod_line_bulk_add(&bulk, line); } - flags = active_low ? GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW : 0; + conf.flags = active_low ? GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW : 0; + conf.consumer = consumer; + + if (event_type == GPIOD_CTXLESS_EVENT_RISING_EDGE) { + conf.request_type = GPIOD_LINE_REQUEST_EVENT_RISING_EDGE; + } else if (event_type == GPIOD_CTXLESS_EVENT_FALLING_EDGE) { + conf.request_type = GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE; + } else if (event_type == GPIOD_CTXLESS_EVENT_BOTH_EDGES) { + conf.request_type = GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES; + } else { + errno = -EINVAL; + ret = -1; + goto out; + } - rv = gpiod_line_request_bulk_both_edges_events_flags(&bulk, - consumer, flags); + rv = gpiod_line_request_bulk(&bulk, &conf, NULL); if (rv) { ret = -1; goto out; diff --git a/src/tools/gpiomon.c b/src/tools/gpiomon.c index 298062a..3c42018 100644 --- a/src/tools/gpiomon.c +++ b/src/tools/gpiomon.c @@ -55,9 +55,6 @@ static void print_help(void) } struct mon_ctx { - bool watch_rising; - bool watch_falling; - unsigned int offset; unsigned int events_wanted; unsigned int events_done; @@ -203,14 +200,14 @@ static int event_callback(int event_type, unsigned int line_offset, switch (event_type) { case GPIOD_CTXLESS_EVENT_CB_RISING_EDGE: - if (ctx->watch_rising) - handle_event(ctx, event_type, line_offset, timestamp); - break; case GPIOD_CTXLESS_EVENT_CB_FALLING_EDGE: - if (ctx->watch_falling) - handle_event(ctx, event_type, line_offset, timestamp); + handle_event(ctx, event_type, line_offset, timestamp); break; default: + /* + * REVISIT: This happening would indicate a problem in the + * library. + */ return GPIOD_CTXLESS_EVENT_CB_RET_OK; } @@ -242,11 +239,10 @@ static int make_signalfd(void) int main(int argc, char **argv) { - unsigned int offsets[GPIOD_LINE_BULK_MAX_LINES]; + unsigned int offsets[GPIOD_LINE_BULK_MAX_LINES], num_lines = 0, offset; + bool active_low = false, watch_rising = false, watch_falling = false; struct timespec timeout = { 10, 0 }; - unsigned int num_lines = 0, offset; - bool active_low = false; - int optc, opti, ret, i; + int optc, opti, ret, i, event_type; struct mon_ctx ctx; char *end; @@ -276,10 +272,10 @@ int main(int argc, char **argv) ctx.silent = true; break; case 'r': - ctx.watch_rising = true; + watch_rising = true; break; case 'f': - ctx.watch_falling = true; + watch_falling = true; break; case 'F': ctx.fmt = optarg; @@ -294,8 +290,12 @@ int main(int argc, char **argv) argc -= optind; argv += optind; - if (!ctx.watch_rising && !ctx.watch_falling) - ctx.watch_rising = ctx.watch_falling = true; + if (watch_rising && !watch_falling) + event_type = GPIOD_CTXLESS_EVENT_RISING_EDGE; + else if (watch_falling && !watch_rising) + event_type = GPIOD_CTXLESS_EVENT_FALLING_EDGE; + else + event_type = GPIOD_CTXLESS_EVENT_BOTH_EDGES; if (argc < 1) die("gpiochip must be specified"); @@ -314,10 +314,11 @@ int main(int argc, char **argv) ctx.sigfd = make_signalfd(); - ret = gpiod_ctxless_event_loop_multiple(argv[0], offsets, num_lines, - active_low, "gpiomon", - &timeout, poll_callback, - event_callback, &ctx); + ret = gpiod_ctxless_event_monitor_multiple(argv[0], event_type, + offsets, num_lines, + active_low, "gpiomon", + &timeout, poll_callback, + event_callback, &ctx); if (ret) die_perror("error waiting for events"); diff --git a/tests/tests-ctxless.c b/tests/tests-ctxless.c index db8585d..ea9403d 100644 --- a/tests/tests-ctxless.c +++ b/tests/tests-ctxless.c @@ -142,7 +142,7 @@ static int ctxless_event_cb(int evtype, unsigned int offset, : GPIOD_CTXLESS_EVENT_CB_RET_OK; } -static void ctxless_event_loop(void) +static void ctxless_event_monitor(void) { struct ctxless_event_data evdata = { false, false, 0, 0 }; struct timespec ts = { 1, 0 }; @@ -150,9 +150,10 @@ static void ctxless_event_loop(void) test_set_event(0, 3, TEST_EVENT_ALTERNATING, 100); - status = gpiod_ctxless_event_loop(test_chip_name(0), 3, false, - TEST_CONSUMER, &ts, NULL, - ctxless_event_cb, &evdata); + status = gpiod_ctxless_event_monitor(test_chip_name(0), + GPIOD_CTXLESS_EVENT_BOTH_EDGES, + 3, false, TEST_CONSUMER, &ts, + NULL, ctxless_event_cb, &evdata); TEST_ASSERT_RET_OK(status); TEST_ASSERT(evdata.got_rising_edge); @@ -160,11 +161,34 @@ static void ctxless_event_loop(void) TEST_ASSERT_EQ(evdata.count, 2); TEST_ASSERT_EQ(evdata.offset, 3); } -TEST_DEFINE(ctxless_event_loop, - "gpiod_ctxless_event_loop() - single event", +TEST_DEFINE(ctxless_event_monitor, + "gpiod_ctxless_event_monitor() - single event", 0, { 8 }); -static void ctxless_event_loop_multiple(void) +static void ctxless_event_monitor_single_event_type(void) +{ + struct ctxless_event_data evdata = { false, false, 0, 0 }; + struct timespec ts = { 1, 0 }; + int rv; + + test_set_event(0, 3, TEST_EVENT_ALTERNATING, 100); + + rv = gpiod_ctxless_event_monitor(test_chip_name(0), + GPIOD_CTXLESS_EVENT_FALLING_EDGE, + 3, false, TEST_CONSUMER, &ts, + NULL, ctxless_event_cb, &evdata); + + TEST_ASSERT_RET_OK(rv); + TEST_ASSERT(evdata.got_falling_edge); + TEST_ASSERT_FALSE(evdata.got_rising_edge); + TEST_ASSERT_EQ(evdata.count, 2); + TEST_ASSERT_EQ(evdata.offset, 3); +} +TEST_DEFINE(ctxless_event_monitor_single_event_type, + "gpiod_ctxless_event_monitor() - specify event type", + 0, { 8 }); + +static void ctxless_event_monitor_multiple(void) { struct ctxless_event_data evdata = { false, false, 0, 0 }; struct timespec ts = { 1, 0 }; @@ -178,10 +202,11 @@ static void ctxless_event_loop_multiple(void) test_set_event(0, 3, TEST_EVENT_ALTERNATING, 100); - status = gpiod_ctxless_event_loop_multiple(test_chip_name(0), offsets, - 4, false, TEST_CONSUMER, - &ts, NULL, ctxless_event_cb, - &evdata); + status = gpiod_ctxless_event_monitor_multiple( + test_chip_name(0), + GPIOD_CTXLESS_EVENT_BOTH_EDGES, + offsets, 4, false, TEST_CONSUMER, + &ts, NULL, ctxless_event_cb, &evdata); TEST_ASSERT_RET_OK(status); TEST_ASSERT(evdata.got_rising_edge); @@ -189,8 +214,8 @@ static void ctxless_event_loop_multiple(void) TEST_ASSERT_EQ(evdata.count, 2); TEST_ASSERT_EQ(evdata.offset, 3); } -TEST_DEFINE(ctxless_event_loop_multiple, - "gpiod_ctxless_event_loop_multiple() - single event", +TEST_DEFINE(ctxless_event_monitor_multiple, + "gpiod_ctxless_event_monitor_multiple() - single event", 0, { 8 }); static int error_event_cb(int evtype TEST_UNUSED, @@ -203,38 +228,40 @@ static int error_event_cb(int evtype TEST_UNUSED, return GPIOD_CTXLESS_EVENT_CB_RET_ERR; } -static void ctxless_event_loop_indicate_error(void) +static void ctxless_event_monitor_indicate_error(void) { struct timespec ts = { 1, 0 }; int rv; test_set_event(0, 3, TEST_EVENT_ALTERNATING, 100); - rv = gpiod_ctxless_event_loop(test_chip_name(0), 3, false, - TEST_CONSUMER, &ts, NULL, - error_event_cb, NULL); + rv = gpiod_ctxless_event_monitor(test_chip_name(0), + GPIOD_CTXLESS_EVENT_BOTH_EDGES, + 3, false, TEST_CONSUMER, &ts, + NULL, error_event_cb, NULL); TEST_ASSERT_EQ(rv, -1); TEST_ASSERT_ERRNO_IS(ENOTBLK); } -TEST_DEFINE(ctxless_event_loop_indicate_error, - "gpiod_ctxless_event_loop() - error in callback", +TEST_DEFINE(ctxless_event_monitor_indicate_error, + "gpiod_ctxless_event_monitor() - error in callback", 0, { 8 }); -static void ctxless_event_loop_indicate_error_timeout(void) +static void ctxless_event_monitor_indicate_error_timeout(void) { struct timespec ts = { 0, 100000 }; int rv; - rv = gpiod_ctxless_event_loop(test_chip_name(0), 3, false, - TEST_CONSUMER, &ts, NULL, - error_event_cb, NULL); + rv = gpiod_ctxless_event_monitor(test_chip_name(0), + GPIOD_CTXLESS_EVENT_BOTH_EDGES, + 3, false, TEST_CONSUMER, &ts, + NULL, error_event_cb, NULL); TEST_ASSERT_EQ(rv, -1); TEST_ASSERT_ERRNO_IS(ENOTBLK); } -TEST_DEFINE(ctxless_event_loop_indicate_error_timeout, - "gpiod_ctxless_event_loop() - error in callback after timeout", +TEST_DEFINE(ctxless_event_monitor_indicate_error_timeout, + "gpiod_ctxless_event_monitor() - error in callback after timeout", 0, { 8 }); static void ctxless_find_line_good(void) |