@@ -41,6 +41,7 @@ extern "C" {
struct gpiod_chip;
struct gpiod_line_info;
+struct gpiod_watch_event;
struct gpiod_line_attr;
struct gpiod_line_config;
struct gpiod_request_config;
@@ -107,6 +108,49 @@ unsigned int gpiod_chip_get_num_lines(struct gpiod_chip *chip);
struct gpiod_line_info *gpiod_chip_get_line_info(struct gpiod_chip *chip,
unsigned int offset);
+/**
+ * @brief Get the current snapshot of information about the line at given
+ * offset and start watching it for future changes.
+ * @param chip GPIO chip object.
+ * @param offset The offset of the GPIO line.
+ * @return New GPIO line info object or NULL if an error occurred.
+ */
+struct gpiod_line_info *gpiod_chip_watch_line_info(struct gpiod_chip *chip,
+ unsigned int offset);
+
+/**
+ * @brief Stop watching the line at given offset for status changes.
+ * @param chip GPIO chip object.
+ * @param offset The offset of the GPIO line.
+ * @return 0 on success, -1 on failure.
+ */
+int gpiod_chip_unwatch_line_info(struct gpiod_chip *chip, unsigned int offset);
+
+/**
+ * @brief Get the file descriptor associated with this chip.
+ * @param chip GPIO chip object.
+ * @return File descriptor number. This function never fails.
+ */
+int gpiod_chip_get_fd(struct gpiod_chip *chip);
+
+/**
+ * @brief Wait for line status events on any of the lines associated with
+ * this chip.
+ * @param chip GPIO chip object.
+ * @param timeout Wait time limit in nanoseconds.
+ * @return 0 if wait timed out, -1 if an error occurred, 1 if an event is
+ * pending.
+ */
+int gpiod_chip_watch_event_wait(struct gpiod_chip *chip, uint64_t timeout);
+
+/**
+ * @brief Read a single line status change event from this chip.
+ * @param chip GPIO chip object.
+ * @return Newly read watch event object or NULL on error.
+ * @note If no events are pending, this function will block.
+ */
+struct gpiod_watch_event *gpiod_chip_watch_event_read(struct gpiod_chip *chip);
+
/**
* @brief Map a GPIO line's name to its offset within the chip.
* @param chip GPIO chip object.
@@ -295,6 +339,65 @@ bool gpiod_line_is_debounced(struct gpiod_line_info *info);
unsigned long
gpiod_line_get_debounce_period(struct gpiod_line_info *info);
+/**
+ * @}
+ *
+ * @defgroup line_watch Line status watch events
+ * @{
+ */
+
+/**
+ * @brief Possible line status change event types.
+ */
+enum {
+ GPIOD_WATCH_EVENT_LINE_REQUESTED = 1,
+ /**< Line has been requested. */
+ GPIOD_WATCH_EVENT_LINE_RELEASED,
+ /**< Previously requested line has been released. */
+ GPIOD_WATCH_EVENT_LINE_CONFIG_CHANGED,
+ /**< Line configuration has changed. */
+};
+
+/**
+ * @brief Increase the reference count of this line status watch event.
+ * @param event Line status watch event.
+ * @return Passed reference to this line watch event.
+ */
+struct gpiod_watch_event *
+gpiod_watch_event_ref(struct gpiod_watch_event *event);
+
+/**
+ * @brief Decrease the reference count of this line status watch event. If the
+ * reference count reaches 0, release all associated resources.
+ * @param event Line status watch event.
+ */
+void gpiod_watch_event_unref(struct gpiod_watch_event *event);
+
+/**
+ * @brief Get the event type of this status change event.
+ * @param event Line status watch event.
+ * @return One of ::GPIOD_WATCH_EVENT_LINE_REQUESTED,
+ * ::GPIOD_WATCH_EVENT_LINE_RELEASED or
+ * ::GPIOD_WATCH_EVENT_LINE_CONFIG_CHANGED.
+ */
+int gpiod_watch_event_get_event_type(struct gpiod_watch_event *event);
+
+/**
+ * @brief Get the line info snapshot at the time of the watch event occurence.
+ * @param event Line status watch event.
+ * @return Reference to the line info object whose lifetime must be managed
+ * by the caller.
+ */
+struct gpiod_line_info *
+gpiod_watch_event_get_line_info(struct gpiod_watch_event *event);
+
+/**
+ * @brief Get the timestamp of the event.
+ * @param event Line status watch event.
+ * @return Timestamp in nanoseconds.
+ */
+uint64_t gpiod_watch_event_get_timestamp(struct gpiod_watch_event *event);
+
/**
* @}
*
@@ -13,7 +13,8 @@ libgpiod_la_SOURCES = attr.c \
mask.c \
misc.c \
request.c \
- uapi/gpio.h
+ uapi/gpio.h \
+ watch.c
libgpiod_la_CFLAGS = -Wall -Wextra -g -std=gnu89
libgpiod_la_CFLAGS += -fvisibility=hidden -I$(top_srcdir)/include/
@@ -117,33 +117,84 @@ GPIOD_API unsigned int gpiod_chip_get_num_lines(struct gpiod_chip *chip)
}
static int chip_read_line_info(int fd, unsigned int offset,
- struct gpio_v2_line_info *infobuf)
+ struct gpio_v2_line_info *infobuf, bool watch)
{
- int ret;
+ int ret, cmd;
memset(infobuf, 0, sizeof(*infobuf));
infobuf->offset = offset;
- ret = ioctl(fd, GPIO_V2_GET_LINEINFO_IOCTL, infobuf);
+ cmd = watch ? GPIO_V2_GET_LINEINFO_WATCH_IOCTL :
+ GPIO_V2_GET_LINEINFO_IOCTL;
+
+ ret = ioctl(fd, cmd, infobuf);
if (ret)
return -1;
return 0;
}
-GPIOD_API struct gpiod_line_info *
-gpiod_chip_get_line_info(struct gpiod_chip *chip, unsigned int offset)
+static struct gpiod_line_info *
+chip_get_line_info(struct gpiod_chip *chip, unsigned int offset, bool watch)
{
struct gpio_v2_line_info infobuf;
int ret;
- ret = chip_read_line_info(chip->fd, offset, &infobuf);
+ ret = chip_read_line_info(chip->fd, offset, &infobuf, watch);
if (ret)
return NULL;
return gpiod_line_info_from_kernel(&infobuf);
}
+GPIOD_API struct gpiod_line_info *
+gpiod_chip_get_line_info(struct gpiod_chip *chip, unsigned int offset)
+{
+ return chip_get_line_info(chip, offset, false);
+}
+
+GPIOD_API struct gpiod_line_info *
+gpiod_chip_watch_line_info(struct gpiod_chip *chip, unsigned int offset)
+{
+ return chip_get_line_info(chip, offset, true);
+}
+
+GPIOD_API int gpiod_chip_unwatch_line_info(struct gpiod_chip *chip,
+ unsigned int offset)
+{
+ return ioctl(chip->fd, GPIO_GET_LINEINFO_UNWATCH_IOCTL, &offset);
+}
+
+GPIOD_API int gpiod_chip_get_fd(struct gpiod_chip *chip)
+{
+ return chip->fd;
+}
+
+GPIOD_API int gpiod_chip_watch_event_wait(struct gpiod_chip *chip,
+ uint64_t timeout)
+{
+ return gpiod_poll_fd(chip->fd, timeout);
+}
+
+GPIOD_API struct gpiod_watch_event *
+gpiod_chip_watch_event_read(struct gpiod_chip *chip)
+{
+ struct gpio_v2_line_info_changed evbuf;
+ ssize_t rd;
+
+ memset(&evbuf, 0, sizeof(evbuf));
+
+ rd = read(chip->fd, &evbuf, sizeof(evbuf));
+ if (rd < 0) {
+ return NULL;
+ } else if ((unsigned int)rd < sizeof(evbuf)) {
+ errno = EIO;
+ return NULL;
+ }
+
+ return gpiod_watch_event_from_kernel(&evbuf);
+}
+
GPIOD_API int gpiod_chip_find_line(struct gpiod_chip *chip, const char *name)
{
struct gpio_v2_line_info infobuf;
@@ -151,7 +202,7 @@ GPIOD_API int gpiod_chip_find_line(struct gpiod_chip *chip, const char *name)
int ret;
for (offset = 0; offset < chip->num_lines; offset++) {
- ret = chip_read_line_info(chip->fd, offset, &infobuf);
+ ret = chip_read_line_info(chip->fd, offset, &infobuf, false);
if (ret)
return -1;
@@ -5,7 +5,6 @@
#include <errno.h>
#include <gpiod.h>
-#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
@@ -110,24 +109,7 @@ GPIOD_API int
gpiod_request_handle_event_wait(struct gpiod_request_handle *handle,
uint64_t timeout)
{
- struct timespec ts;
- struct pollfd pfd;
- int ret;
-
- memset(&pfd, 0, sizeof(pfd));
- pfd.fd = handle->fd;
- pfd.events = POLLIN | POLLPRI;
-
- ts.tv_sec = timeout / 1000000000ULL;
- ts.tv_nsec = timeout % 1000000000ULL;
-
- ret = ppoll(&pfd, 1, &ts, NULL);
- if (ret < 0)
- return -1;
- else if (ret == 0)
- return 0;
-
- return 1;
+ return gpiod_poll_fd(handle->fd, timeout);
}
GPIOD_API int
@@ -2,6 +2,8 @@
// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl>
#include <errno.h>
+#include <poll.h>
+#include <string.h>
#include "internal.h"
@@ -121,3 +123,25 @@ uint64_t gpiod_make_kernel_flags(int request_type, int drive, int bias,
return flags;
}
+
+int gpiod_poll_fd(int fd, uint64_t timeout)
+{
+ struct timespec ts;
+ struct pollfd pfd;
+ int ret;
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = fd;
+ pfd.events = POLLIN | POLLPRI;
+
+ ts.tv_sec = timeout / 1000000000ULL;
+ ts.tv_nsec = timeout % 1000000000ULL;
+
+ ret = ppoll(&pfd, 1, &ts, NULL);
+ if (ret < 0)
+ return -1;
+ else if (ret == 0)
+ return 0;
+
+ return 1;
+}
@@ -42,9 +42,13 @@ void gpiod_line_attr_to_kernel(struct gpiod_line_attr *attr,
uint64_t gpiod_make_kernel_flags(int request_type, int drive, int bias,
bool active_low, bool clock_realtime);
struct gpiod_request_handle *gpiod_request_handle_from_fd(int fd);
+struct gpiod_watch_event *
+gpiod_watch_event_from_kernel(struct gpio_v2_line_info_changed *evbuf);
int gpiod_validate_request_type(int request_type);
int gpiod_validate_drive(int drive);
int gpiod_validate_bias(int bias);
+int gpiod_poll_fd(int fd, uint64_t timeout);
+
#endif /* __LIBGPIOD_GPIOD_INTERNAL_H__ */
new file mode 100644
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl>
+
+/* Line status watch. */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "internal.h"
+
+struct gpiod_watch_event {
+ struct gpiod_refcount refcount;
+ int event_type;
+ uint64_t timestamp;
+ struct gpiod_line_info *info;
+};
+
+static void watch_event_release(struct gpiod_refcount *refcount)
+{
+ struct gpiod_watch_event *event;
+
+ event = gpiod_container_of(refcount,
+ struct gpiod_watch_event, refcount);
+
+ gpiod_line_info_unref(event->info);
+ free(event);
+}
+
+struct gpiod_watch_event *
+gpiod_watch_event_from_kernel(struct gpio_v2_line_info_changed *evbuf)
+{
+ struct gpiod_watch_event *event;
+
+ event = malloc(sizeof(*event));
+ if (!event)
+ return NULL;
+
+ memset(event, 0, sizeof(*event));
+ gpiod_refcount_init(&event->refcount, watch_event_release);
+ event->timestamp = evbuf->timestamp_ns;
+
+ switch (evbuf->event_type) {
+ case GPIOLINE_CHANGED_REQUESTED:
+ event->event_type = GPIOD_WATCH_EVENT_LINE_REQUESTED;
+ break;
+ case GPIOLINE_CHANGED_RELEASED:
+ event->event_type = GPIOD_WATCH_EVENT_LINE_RELEASED;
+ break;
+ case GPIOLINE_CHANGED_CONFIG:
+ event->event_type = GPIOD_WATCH_EVENT_LINE_CONFIG_CHANGED;
+ break;
+ default:
+ /* Can't happen unless there's a bug in the kernel. */
+ errno = ENOMSG;
+ free(event);
+ return NULL;
+ }
+
+ event->info = gpiod_line_info_from_kernel(&evbuf->info);
+ if (!event->info) {
+ free(event);
+ return NULL;
+ }
+
+ return event;
+}
+
+GPIOD_API struct gpiod_watch_event *
+gpiod_watch_event_ref(struct gpiod_watch_event *event)
+{
+ gpiod_refcount_ref(&event->refcount);
+ return event;
+}
+
+GPIOD_API void gpiod_watch_event_unref(struct gpiod_watch_event *event)
+{
+ gpiod_refcount_unref(&event->refcount);
+}
+
+GPIOD_API int gpiod_watch_event_get_event_type(struct gpiod_watch_event *event)
+{
+ return event->event_type;
+}
+
+GPIOD_API struct gpiod_line_info *
+gpiod_watch_event_get_line_info(struct gpiod_watch_event *event)
+{
+ return gpiod_line_info_ref(event->info);
+}
+
+GPIOD_API uint64_t
+gpiod_watch_event_get_timestamp(struct gpiod_watch_event *event)
+{
+ return event->timestamp;
+}
This implement support for line status watch events in libgpiod. Unlike line events, these are not expected to happen very often and continuously so there's no need for a dedicated container. Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl> --- include/gpiod.h | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/Makefile.am | 3 +- lib/chip.c | 65 ++++++++++++++++++++++++++---- lib/handle.c | 20 +--------- lib/internal.c | 24 +++++++++++ lib/internal.h | 4 ++ lib/watch.c | 96 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 288 insertions(+), 27 deletions(-) create mode 100644 lib/watch.c