From: Bartosz Golaszewski <brgl@bgdev.pl>
To: Kent Gibson <warthog618@gmail.com>,
Linus Walleij <linus.walleij@linaro.org>,
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: linux-gpio@vger.kernel.org, Bartosz Golaszewski <brgl@bgdev.pl>
Subject: [libgpiod][RFC 6/6] core: implement line watch events
Date: Sat, 10 Apr 2021 16:51:57 +0200 [thread overview]
Message-ID: <20210410145157.30718-7-brgl@bgdev.pl> (raw)
In-Reply-To: <20210410145157.30718-1-brgl@bgdev.pl>
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
diff --git a/include/gpiod.h b/include/gpiod.h
index 8831d4c..5202ede 100644
--- a/include/gpiod.h
+++ b/include/gpiod.h
@@ -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);
+
/**
* @}
*
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 8713d52..5d9b998 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -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/
diff --git a/lib/chip.c b/lib/chip.c
index 9bacfe7..6fb4c1b 100644
--- a/lib/chip.c
+++ b/lib/chip.c
@@ -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;
diff --git a/lib/handle.c b/lib/handle.c
index abd08f9..9fccb7c 100644
--- a/lib/handle.c
+++ b/lib/handle.c
@@ -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
diff --git a/lib/internal.c b/lib/internal.c
index 06adcd2..66a42e6 100644
--- a/lib/internal.c
+++ b/lib/internal.c
@@ -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;
+}
diff --git a/lib/internal.h b/lib/internal.h
index 2677ee3..fa2bc04 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -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__ */
diff --git a/lib/watch.c b/lib/watch.c
new file mode 100644
index 0000000..bc1721d
--- /dev/null
+++ b/lib/watch.c
@@ -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;
+}
--
2.30.1
next prev parent reply other threads:[~2021-04-10 14:52 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-04-10 14:51 [libgpiod][RFC 0/6] first draft of libgpiod v2.0 API Bartosz Golaszewski
2021-04-10 14:51 ` [libgpiod][RFC 1/6] treewide: rename chip property accessors Bartosz Golaszewski
2021-04-10 14:51 ` [libgpiod][RFC 2/6] core: add refcounting helpers Bartosz Golaszewski
2021-04-10 14:51 ` [libgpiod][RFC 3/6] core: implement line_info objects Bartosz Golaszewski
2021-04-10 14:51 ` [libgpiod][RFC 4/6] core: rework line events Bartosz Golaszewski
2021-04-10 14:51 ` [libgpiod][RFC 5/6] core: rework line requests Bartosz Golaszewski
2021-04-10 14:51 ` Bartosz Golaszewski [this message]
2021-04-14 14:15 ` [libgpiod][RFC 0/6] first draft of libgpiod v2.0 API Kent Gibson
2021-04-16 9:36 ` Bartosz Golaszewski
2021-04-17 7:23 ` Kent Gibson
2021-04-17 11:31 ` Bartosz Golaszewski
2021-04-18 3:48 ` Kent Gibson
2021-04-18 21:12 ` Bartosz Golaszewski
2021-04-19 1:17 ` Kent Gibson
2021-04-21 20:04 ` Bartosz Golaszewski
2021-04-22 2:32 ` Kent Gibson
2021-04-22 9:24 ` Bartosz Golaszewski
2021-04-23 1:38 ` Kent Gibson
2021-04-28 9:19 ` Bartosz Golaszewski
2021-04-28 10:34 ` Kent Gibson
2021-04-30 17:52 ` Bartosz Golaszewski
2021-05-01 0:15 ` Kent Gibson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210410145157.30718-7-brgl@bgdev.pl \
--to=brgl@bgdev.pl \
--cc=andriy.shevchenko@linux.intel.com \
--cc=linus.walleij@linaro.org \
--cc=linux-gpio@vger.kernel.org \
--cc=warthog618@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.