ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = foreign
-SUBDIRS = include src bindings
+SUBDIRS = include lib bindings
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libgpiod.pc
-
-if WITH_TESTS
+if WITH_TOOLS
-SUBDIRS += tests
+SUBDIRS += tools man
endif
-if WITH_TOOLS
+if WITH_TESTS
-SUBDIRS += man
+SUBDIRS += tests
endif
EXTRA_DIST = Doxyfile
endif
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libgpiod.pc
libgpiodcxx_la_CPPFLAGS = -Wall -Wextra -g -std=gnu++11
libgpiodcxx_la_CPPFLAGS += -fvisibility=hidden -I$(top_srcdir)/include/
libgpiodcxx_la_LDFLAGS = -version-info $(subst .,:,$(ABI_CXX_VERSION))
-libgpiodcxx_la_LDFLAGS += -lgpiod -L$(top_builddir)/src/lib
+libgpiodcxx_la_LDFLAGS += -lgpiod -L$(top_builddir)/lib
include_HEADERS = gpiod.hpp
gpiod_la_CFLAGS = -I$(top_srcdir)/include/
gpiod_la_CFLAGS += -Wall -Wextra -g $(PYTHON_CPPFLAGS)
gpiod_la_LDFLAGS = -module -avoid-version
-gpiod_la_LIBADD = $(top_builddir)/src/lib/libgpiod.la $(PYTHON_LIBS)
+gpiod_la_LIBADD = $(top_builddir)/lib/libgpiod.la $(PYTHON_LIBS)
AC_ARG_VAR([PYTHON_LIBS],
[Libraries to link into Python extensions [default: auto-detect]])
-AC_CONFIG_SRCDIR([src])
+AC_CONFIG_SRCDIR([lib])
AC_CONFIG_HEADER([config.h])
AC_DEFINE([_GNU_SOURCE], [], [We want GNU extensions])
AC_CONFIG_FILES([libgpiod.pc
Makefile
include/Makefile
- src/Makefile
- src/lib/Makefile
- src/tools/Makefile
+ lib/Makefile
+ tools/Makefile
tests/Makefile
bindings/cxx/libgpiodcxx.pc
bindings/Makefile
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+#
+# This file is part of libgpiod.
+#
+# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+
+lib_LTLIBRARIES = libgpiod.la
+libgpiod_la_SOURCES = core.c ctxless.c helpers.c iter.c misc.c
+libgpiod_la_CFLAGS = -Wall -Wextra -g
+libgpiod_la_CFLAGS += -fvisibility=hidden -I$(top_srcdir)/include/
+libgpiod_la_CFLAGS += -include $(top_builddir)/config.h
+libgpiod_la_LDFLAGS = -version-info $(subst .,:,$(ABI_VERSION))
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+/* Low-level, core library code. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <gpiod.h>
+#include <linux/gpio.h>
+#include <poll.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+enum {
+ LINE_FREE = 0,
+ LINE_REQUESTED_VALUES,
+ LINE_REQUESTED_EVENTS,
+};
+
+struct line_fd_handle {
+ int fd;
+ int refcount;
+};
+
+struct gpiod_line {
+ unsigned int offset;
+ int direction;
+ int active_state;
+ bool used;
+ bool open_source;
+ bool open_drain;
+
+ int state;
+ bool up_to_date;
+
+ struct gpiod_chip *chip;
+ struct line_fd_handle *fd_handle;
+
+ char name[32];
+ char consumer[32];
+};
+
+struct gpiod_chip {
+ struct gpiod_line **lines;
+ unsigned int num_lines;
+
+ int fd;
+
+ char name[32];
+ char label[32];
+};
+
+static bool is_gpiochip_cdev(const char *path)
+{
+ char *name, *pathcpy, *sysfsp, sysfsdev[16], devstr[16];
+ struct stat statbuf;
+ bool ret = false;
+ int rv, fd;
+ ssize_t rd;
+
+ rv = lstat(path, &statbuf);
+ if (rv)
+ goto out;
+
+ /* Is it a character device? */
+ if (!S_ISCHR(statbuf.st_mode)) {
+ /*
+ * Passing a file descriptor not associated with a character
+ * device to ioctl() makes it set errno to ENOTTY. Let's do
+ * the same in order to stay compatible with the versions of
+ * libgpiod from before the introduction of this routine.
+ */
+ errno = ENOTTY;
+ goto out;
+ }
+
+ /* Get the basename. */
+ pathcpy = strdup(path);
+ if (!pathcpy)
+ goto out;
+
+ name = basename(pathcpy);
+
+ /* Do we have a corresponding sysfs attribute? */
+ rv = asprintf(&sysfsp, "/sys/bus/gpio/devices/%s/dev", name);
+ if (rv < 0)
+ goto out_free_pathcpy;
+
+ if (access(sysfsp, R_OK) != 0) {
+ /*
+ * This is a character device but not the one we're after.
+ * Before the introduction of this function, we'd fail with
+ * ENOTTY on the first GPIO ioctl() call for this file
+ * descriptor. Let's stay compatible here and keep returning
+ * the same error code.
+ */
+ errno = ENOTTY;
+ goto out_free_sysfsp;
+ }
+
+ /*
+ * Make sure the major and minor numbers of the character device
+ * correspond with the ones in the dev attribute in sysfs.
+ */
+ snprintf(devstr, sizeof(devstr), "%u:%u",
+ major(statbuf.st_rdev), minor(statbuf.st_rdev));
+
+ fd = open(sysfsp, O_RDONLY);
+ if (fd < 0)
+ goto out_free_sysfsp;
+
+ memset(sysfsdev, 0, sizeof(sysfsdev));
+ rd = read(fd, sysfsdev, strlen(devstr));
+ close(fd);
+ if (rd < 0)
+ goto out_free_sysfsp;
+
+ if (strcmp(sysfsdev, devstr) != 0) {
+ errno = ENODEV;
+ goto out_free_sysfsp;
+ }
+
+ ret = true;
+
+out_free_sysfsp:
+ free(sysfsp);
+out_free_pathcpy:
+ free(pathcpy);
+out:
+ return ret;
+}
+
+struct gpiod_chip *gpiod_chip_open(const char *path)
+{
+ struct gpiochip_info info;
+ struct gpiod_chip *chip;
+ int rv, fd;
+
+ fd = open(path, O_RDWR | O_CLOEXEC);
+ if (fd < 0)
+ return NULL;
+
+ /*
+ * We were able to open the file but is it really a gpiochip character
+ * device?
+ */
+ if (!is_gpiochip_cdev(path)) {
+ close(fd);
+ return NULL;
+ }
+
+ chip = malloc(sizeof(*chip));
+ if (!chip)
+ goto err_close_fd;
+
+ memset(chip, 0, sizeof(*chip));
+ memset(&info, 0, sizeof(info));
+
+ rv = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);
+ if (rv < 0)
+ goto err_free_chip;
+
+ chip->fd = fd;
+ chip->num_lines = info.lines;
+
+ /*
+ * GPIO device must have a name - don't bother checking this field. In
+ * the worst case (would have to be a weird kernel bug) it'll be empty.
+ */
+ strncpy(chip->name, info.name, sizeof(chip->name));
+
+ /*
+ * The kernel sets the label of a GPIO device to "unknown" if it
+ * hasn't been defined in DT, board file etc. On the off-chance that
+ * we got an empty string, do the same.
+ */
+ if (info.label[0] == '\0')
+ strncpy(chip->label, "unknown", sizeof(chip->label));
+ else
+ strncpy(chip->label, info.label, sizeof(chip->label));
+
+ return chip;
+
+err_free_chip:
+ free(chip);
+err_close_fd:
+ close(fd);
+
+ return NULL;
+}
+
+void gpiod_chip_close(struct gpiod_chip *chip)
+{
+ struct gpiod_line *line;
+ unsigned int i;
+
+ if (chip->lines) {
+ for (i = 0; i < chip->num_lines; i++) {
+ line = chip->lines[i];
+ if (line) {
+ gpiod_line_release(line);
+ free(line);
+ }
+ }
+
+ free(chip->lines);
+ }
+
+ close(chip->fd);
+ free(chip);
+}
+
+const char *gpiod_chip_name(struct gpiod_chip *chip)
+{
+ return chip->name;
+}
+
+const char *gpiod_chip_label(struct gpiod_chip *chip)
+{
+ return chip->label;
+}
+
+unsigned int gpiod_chip_num_lines(struct gpiod_chip *chip)
+{
+ return chip->num_lines;
+}
+
+struct gpiod_line *
+gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset)
+{
+ struct gpiod_line *line;
+ int rv;
+
+ if (offset >= chip->num_lines) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!chip->lines) {
+ chip->lines = calloc(chip->num_lines,
+ sizeof(struct gpiod_line *));
+ if (!chip->lines)
+ return NULL;
+ }
+
+ if (!chip->lines[offset]) {
+ line = malloc(sizeof(*line));
+ if (!line)
+ return NULL;
+
+ memset(line, 0, sizeof(*line));
+
+ line->offset = offset;
+ line->chip = chip;
+
+ chip->lines[offset] = line;
+ } else {
+ line = chip->lines[offset];
+ }
+
+ rv = gpiod_line_update(line);
+ if (rv < 0)
+ return NULL;
+
+ return line;
+}
+
+static struct line_fd_handle *line_make_fd_handle(int fd)
+{
+ struct line_fd_handle *handle;
+
+ handle = malloc(sizeof(*handle));
+ if (!handle)
+ return NULL;
+
+ handle->fd = fd;
+ handle->refcount = 0;
+
+ return handle;
+}
+
+static void line_fd_incref(struct gpiod_line *line)
+{
+ line->fd_handle->refcount++;
+}
+
+static void line_fd_decref(struct gpiod_line *line)
+{
+ struct line_fd_handle *handle = line->fd_handle;
+
+ handle->refcount--;
+
+ if (handle->refcount == 0) {
+ close(handle->fd);
+ free(handle);
+ line->fd_handle = NULL;
+ }
+}
+
+static void line_set_fd(struct gpiod_line *line, struct line_fd_handle *handle)
+{
+ line->fd_handle = handle;
+ line_fd_incref(line);
+}
+
+static int line_get_fd(struct gpiod_line *line)
+{
+ return line->fd_handle->fd;
+}
+
+static void line_maybe_update(struct gpiod_line *line)
+{
+ int rv;
+
+ rv = gpiod_line_update(line);
+ if (rv < 0)
+ line->up_to_date = false;
+}
+
+struct gpiod_chip *gpiod_line_get_chip(struct gpiod_line *line)
+{
+ return line->chip;
+}
+
+unsigned int gpiod_line_offset(struct gpiod_line *line)
+{
+ return line->offset;
+}
+
+const char *gpiod_line_name(struct gpiod_line *line)
+{
+ return line->name[0] == '\0' ? NULL : line->name;
+}
+
+const char *gpiod_line_consumer(struct gpiod_line *line)
+{
+ return line->consumer[0] == '\0' ? NULL : line->consumer;
+}
+
+int gpiod_line_direction(struct gpiod_line *line)
+{
+ return line->direction;
+}
+
+int gpiod_line_active_state(struct gpiod_line *line)
+{
+ return line->active_state;
+}
+
+bool gpiod_line_is_used(struct gpiod_line *line)
+{
+ return line->used;
+}
+
+bool gpiod_line_is_open_drain(struct gpiod_line *line)
+{
+ return line->open_drain;
+}
+
+bool gpiod_line_is_open_source(struct gpiod_line *line)
+{
+ return line->open_source;
+}
+
+bool gpiod_line_needs_update(struct gpiod_line *line)
+{
+ return !line->up_to_date;
+}
+
+int gpiod_line_update(struct gpiod_line *line)
+{
+ struct gpioline_info info;
+ int rv;
+
+ memset(&info, 0, sizeof(info));
+ info.line_offset = line->offset;
+
+ rv = ioctl(line->chip->fd, GPIO_GET_LINEINFO_IOCTL, &info);
+ if (rv < 0)
+ return -1;
+
+ line->direction = info.flags & GPIOLINE_FLAG_IS_OUT
+ ? GPIOD_LINE_DIRECTION_OUTPUT
+ : GPIOD_LINE_DIRECTION_INPUT;
+ line->active_state = info.flags & GPIOLINE_FLAG_ACTIVE_LOW
+ ? GPIOD_LINE_ACTIVE_STATE_LOW
+ : GPIOD_LINE_ACTIVE_STATE_HIGH;
+
+ line->used = info.flags & GPIOLINE_FLAG_KERNEL;
+ line->open_drain = info.flags & GPIOLINE_FLAG_OPEN_DRAIN;
+ line->open_source = info.flags & GPIOLINE_FLAG_OPEN_SOURCE;
+
+ strncpy(line->name, info.name, sizeof(line->name));
+ strncpy(line->consumer, info.consumer, sizeof(line->consumer));
+
+ line->up_to_date = true;
+
+ return 0;
+}
+
+static bool line_bulk_same_chip(struct gpiod_line_bulk *bulk)
+{
+ struct gpiod_line *first_line, *line;
+ struct gpiod_chip *first_chip, *chip;
+ unsigned int i;
+
+ if (bulk->num_lines == 1)
+ return true;
+
+ first_line = gpiod_line_bulk_get_line(bulk, 0);
+ first_chip = gpiod_line_get_chip(first_line);
+
+ for (i = 1; i < bulk->num_lines; i++) {
+ line = bulk->lines[i];
+ chip = gpiod_line_get_chip(line);
+
+ if (first_chip != chip) {
+ errno = EINVAL;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool line_bulk_all_requested(struct gpiod_line_bulk *bulk)
+{
+ struct gpiod_line *line, **lineptr;
+
+ gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
+ if (!gpiod_line_is_requested(line)) {
+ errno = EPERM;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool line_bulk_all_free(struct gpiod_line_bulk *bulk)
+{
+ struct gpiod_line *line, **lineptr;
+
+ gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
+ if (!gpiod_line_is_free(line)) {
+ errno = EBUSY;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int line_request_values(struct gpiod_line_bulk *bulk,
+ const struct gpiod_line_request_config *config,
+ const int *default_vals)
+{
+ struct gpiod_line *line, **lineptr;
+ struct line_fd_handle *line_fd;
+ struct gpiohandle_request req;
+ unsigned int i;
+ int rv, fd;
+
+ if ((config->request_type != GPIOD_LINE_REQUEST_DIRECTION_OUTPUT) &&
+ (config->flags & (GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN |
+ GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE))) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN) &&
+ (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&req, 0, sizeof(req));
+
+ if (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN)
+ req.flags |= GPIOHANDLE_REQUEST_OPEN_DRAIN;
+ if (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)
+ req.flags |= GPIOHANDLE_REQUEST_OPEN_SOURCE;
+ if (config->flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW)
+ req.flags |= GPIOHANDLE_REQUEST_ACTIVE_LOW;
+
+ if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_INPUT)
+ req.flags |= GPIOHANDLE_REQUEST_INPUT;
+ else if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
+ req.flags |= GPIOHANDLE_REQUEST_OUTPUT;
+
+ req.lines = gpiod_line_bulk_num_lines(bulk);
+
+ gpiod_line_bulk_foreach_line_off(bulk, line, i) {
+ req.lineoffsets[i] = gpiod_line_offset(line);
+ if (config->request_type ==
+ GPIOD_LINE_REQUEST_DIRECTION_OUTPUT &&
+ default_vals)
+ req.default_values[i] = !!default_vals[i];
+ }
+
+ if (config->consumer)
+ strncpy(req.consumer_label, config->consumer,
+ sizeof(req.consumer_label) - 1);
+
+ line = gpiod_line_bulk_get_line(bulk, 0);
+ fd = line->chip->fd;
+
+ rv = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
+ if (rv < 0)
+ return -1;
+
+ line_fd = line_make_fd_handle(req.fd);
+ if (!line_fd)
+ return -1;
+
+ gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
+ line->state = LINE_REQUESTED_VALUES;
+ line_set_fd(line, line_fd);
+ line_maybe_update(line);
+ }
+
+ return 0;
+}
+
+static int line_request_event_single(struct gpiod_line *line,
+ const struct gpiod_line_request_config *config)
+{
+ struct line_fd_handle *line_fd;
+ struct gpioevent_request req;
+ int rv;
+
+ memset(&req, 0, sizeof(req));
+
+ if (config->consumer)
+ strncpy(req.consumer_label, config->consumer,
+ sizeof(req.consumer_label) - 1);
+
+ req.lineoffset = gpiod_line_offset(line);
+ req.handleflags |= GPIOHANDLE_REQUEST_INPUT;
+
+ if (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN)
+ req.handleflags |= GPIOHANDLE_REQUEST_OPEN_DRAIN;
+ if (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)
+ req.handleflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE;
+ if (config->flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW)
+ req.handleflags |= GPIOHANDLE_REQUEST_ACTIVE_LOW;
+
+ if (config->request_type == GPIOD_LINE_REQUEST_EVENT_RISING_EDGE)
+ req.eventflags |= GPIOEVENT_REQUEST_RISING_EDGE;
+ else if (config->request_type == GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE)
+ req.eventflags |= GPIOEVENT_REQUEST_FALLING_EDGE;
+ else if (config->request_type == GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES)
+ req.eventflags |= GPIOEVENT_REQUEST_BOTH_EDGES;
+
+ rv = ioctl(line->chip->fd, GPIO_GET_LINEEVENT_IOCTL, &req);
+ if (rv < 0)
+ return -1;
+
+ line_fd = line_make_fd_handle(req.fd);
+ if (!line_fd)
+ return -1;
+
+ line->state = LINE_REQUESTED_EVENTS;
+ line_set_fd(line, line_fd);
+ line_maybe_update(line);
+
+ return 0;
+}
+
+static int line_request_events(struct gpiod_line_bulk *bulk,
+ const struct gpiod_line_request_config *config)
+{
+ struct gpiod_line *line;
+ unsigned int off;
+ int rv, rev;
+
+ gpiod_line_bulk_foreach_line_off(bulk, line, off) {
+ rv = line_request_event_single(line, config);
+ if (rv) {
+ for (rev = off - 1; rev >= 0; rev--) {
+ line = gpiod_line_bulk_get_line(bulk, rev);
+ gpiod_line_release(line);
+ }
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int gpiod_line_request(struct gpiod_line *line,
+ const struct gpiod_line_request_config *config,
+ int default_val)
+{
+ struct gpiod_line_bulk bulk;
+
+ gpiod_line_bulk_init(&bulk);
+ gpiod_line_bulk_add(&bulk, line);
+
+ return gpiod_line_request_bulk(&bulk, config, &default_val);
+}
+
+static bool line_request_is_direction(int request)
+{
+ return request == GPIOD_LINE_REQUEST_DIRECTION_AS_IS ||
+ request == GPIOD_LINE_REQUEST_DIRECTION_INPUT ||
+ request == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT;
+}
+
+static bool line_request_is_events(int request)
+{
+ return request == GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE ||
+ request == GPIOD_LINE_REQUEST_EVENT_RISING_EDGE ||
+ request == GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES;
+}
+
+int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk,
+ const struct gpiod_line_request_config *config,
+ const int *default_vals)
+{
+ if (!line_bulk_same_chip(bulk) || !line_bulk_all_free(bulk))
+ return -1;
+
+ if (line_request_is_direction(config->request_type))
+ return line_request_values(bulk, config, default_vals);
+ else if (line_request_is_events(config->request_type))
+ return line_request_events(bulk, config);
+
+ errno = EINVAL;
+ return -1;
+}
+
+void gpiod_line_release(struct gpiod_line *line)
+{
+ struct gpiod_line_bulk bulk;
+
+ gpiod_line_bulk_init(&bulk);
+ gpiod_line_bulk_add(&bulk, line);
+
+ gpiod_line_release_bulk(&bulk);
+}
+
+void gpiod_line_release_bulk(struct gpiod_line_bulk *bulk)
+{
+ struct gpiod_line *line, **lineptr;
+
+ gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
+ if (line->state != LINE_FREE) {
+ line_fd_decref(line);
+ line->state = LINE_FREE;
+ }
+ }
+}
+
+bool gpiod_line_is_requested(struct gpiod_line *line)
+{
+ return (line->state == LINE_REQUESTED_VALUES ||
+ line->state == LINE_REQUESTED_EVENTS);
+}
+
+bool gpiod_line_is_free(struct gpiod_line *line)
+{
+ return line->state == LINE_FREE;
+}
+
+int gpiod_line_get_value(struct gpiod_line *line)
+{
+ struct gpiod_line_bulk bulk;
+ int rv, value;
+
+ gpiod_line_bulk_init(&bulk);
+ gpiod_line_bulk_add(&bulk, line);
+
+ rv = gpiod_line_get_value_bulk(&bulk, &value);
+ if (rv < 0)
+ return -1;
+
+ return value;
+}
+
+int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, int *values)
+{
+ struct gpiohandle_data data;
+ struct gpiod_line *first;
+ unsigned int i;
+ int rv, fd;
+
+ if (!line_bulk_same_chip(bulk) || !line_bulk_all_requested(bulk))
+ return -1;
+
+ first = gpiod_line_bulk_get_line(bulk, 0);
+
+ memset(&data, 0, sizeof(data));
+
+ fd = line_get_fd(first);
+
+ rv = ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
+ if (rv < 0)
+ return -1;
+
+ for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++)
+ values[i] = data.values[i];
+
+ return 0;
+}
+
+int gpiod_line_set_value(struct gpiod_line *line, int value)
+{
+ struct gpiod_line_bulk bulk;
+
+ gpiod_line_bulk_init(&bulk);
+ gpiod_line_bulk_add(&bulk, line);
+
+ return gpiod_line_set_value_bulk(&bulk, &value);
+}
+
+int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, const int *values)
+{
+ struct gpiohandle_data data;
+ struct gpiod_line *line;
+ unsigned int i;
+ int rv, fd;
+
+ if (!line_bulk_same_chip(bulk) || !line_bulk_all_requested(bulk))
+ return -1;
+
+ memset(&data, 0, sizeof(data));
+
+ for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++)
+ data.values[i] = (uint8_t)!!values[i];
+
+ line = gpiod_line_bulk_get_line(bulk, 0);
+ fd = line_get_fd(line);
+
+ rv = ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
+ if (rv < 0)
+ return -1;
+
+ return 0;
+}
+
+int gpiod_line_event_wait(struct gpiod_line *line,
+ const struct timespec *timeout)
+{
+ struct gpiod_line_bulk bulk;
+
+ gpiod_line_bulk_init(&bulk);
+ gpiod_line_bulk_add(&bulk, line);
+
+ return gpiod_line_event_wait_bulk(&bulk, timeout, NULL);
+}
+
+int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk,
+ const struct timespec *timeout,
+ struct gpiod_line_bulk *event_bulk)
+{
+ struct pollfd fds[GPIOD_LINE_BULK_MAX_LINES];
+ unsigned int off, num_lines;
+ struct gpiod_line *line;
+ int rv;
+
+ if (!line_bulk_same_chip(bulk) || !line_bulk_all_requested(bulk))
+ return -1;
+
+ memset(fds, 0, sizeof(fds));
+ num_lines = gpiod_line_bulk_num_lines(bulk);
+
+ gpiod_line_bulk_foreach_line_off(bulk, line, off) {
+ fds[off].fd = line_get_fd(line);
+ fds[off].events = POLLIN | POLLPRI;
+ }
+
+ rv = ppoll(fds, num_lines, timeout, NULL);
+ if (rv < 0)
+ return -1;
+ else if (rv == 0)
+ return 0;
+
+ if (event_bulk)
+ gpiod_line_bulk_init(event_bulk);
+
+ for (off = 0; off < num_lines; off++) {
+ if (fds[off].revents) {
+ if (fds[off].revents & POLLNVAL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (event_bulk) {
+ line = gpiod_line_bulk_get_line(bulk, off);
+ gpiod_line_bulk_add(event_bulk, line);
+ }
+
+ if (!--rv)
+ break;
+ }
+ }
+
+ return 1;
+}
+
+int gpiod_line_event_read(struct gpiod_line *line,
+ struct gpiod_line_event *event)
+{
+ int fd;
+
+ if (line->state != LINE_REQUESTED_EVENTS) {
+ errno = EPERM;
+ return -1;
+ }
+
+ fd = line_get_fd(line);
+
+ return gpiod_line_event_read_fd(fd, event);
+}
+
+int gpiod_line_event_get_fd(struct gpiod_line *line)
+{
+ if (line->state != LINE_REQUESTED_EVENTS) {
+ errno = EPERM;
+ return -1;
+ }
+
+ return line_get_fd(line);
+}
+
+int gpiod_line_event_read_fd(int fd, struct gpiod_line_event *event)
+{
+ struct gpioevent_data evdata;
+ ssize_t rd;
+
+ memset(&evdata, 0, sizeof(evdata));
+
+ rd = read(fd, &evdata, sizeof(evdata));
+ if (rd < 0) {
+ return -1;
+ } else if (rd != sizeof(evdata)) {
+ errno = EIO;
+ return -1;
+ }
+
+ event->event_type = evdata.id == GPIOEVENT_EVENT_RISING_EDGE
+ ? GPIOD_LINE_EVENT_RISING_EDGE
+ : GPIOD_LINE_EVENT_FALLING_EDGE;
+
+ event->ts.tv_sec = evdata.timestamp / 1000000000ULL;
+ event->ts.tv_nsec = evdata.timestamp % 1000000000ULL;
+
+ return 0;
+}
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+/* Implementation of the high-level API. */
+
+
+#include <errno.h>
+#include <gpiod.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+
+int gpiod_ctxless_get_value(const char *device, unsigned int offset,
+ bool active_low, const char *consumer)
+{
+ int value, rv;
+
+ rv = gpiod_ctxless_get_value_multiple(device, &offset, &value,
+ 1, active_low, consumer);
+ if (rv < 0)
+ return rv;
+
+ return value;
+}
+
+int gpiod_ctxless_get_value_multiple(const char *device,
+ const unsigned int *offsets, int *values,
+ unsigned int num_lines, bool active_low,
+ const char *consumer)
+{
+ struct gpiod_line_bulk bulk;
+ struct gpiod_chip *chip;
+ struct gpiod_line *line;
+ unsigned int i;
+ int rv, flags;
+
+ if (!num_lines || num_lines > GPIOD_LINE_BULK_MAX_LINES) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ chip = gpiod_chip_open_lookup(device);
+ if (!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_LINE_REQUEST_FLAG_ACTIVE_LOW : 0;
+
+ rv = gpiod_line_request_bulk_input_flags(&bulk, consumer, flags);
+ if (rv < 0) {
+ gpiod_chip_close(chip);
+ return -1;
+ }
+
+ memset(values, 0, sizeof(*values) * num_lines);
+ rv = gpiod_line_get_value_bulk(&bulk, values);
+
+ gpiod_chip_close(chip);
+
+ return rv;
+}
+
+int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value,
+ bool active_low, const char *consumer,
+ gpiod_ctxless_set_value_cb cb, void *data)
+{
+ return gpiod_ctxless_set_value_multiple(device, &offset, &value, 1,
+ active_low, consumer, cb, data);
+}
+
+int gpiod_ctxless_set_value_multiple(const char *device,
+ const unsigned int *offsets,
+ const int *values, unsigned int num_lines,
+ bool active_low, const char *consumer,
+ gpiod_ctxless_set_value_cb cb, void *data)
+{
+ struct gpiod_line_bulk bulk;
+ struct gpiod_chip *chip;
+ struct gpiod_line *line;
+ unsigned int i;
+ int rv, flags;
+
+ if (!num_lines || num_lines > GPIOD_LINE_BULK_MAX_LINES) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ chip = gpiod_chip_open_lookup(device);
+ if (!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_LINE_REQUEST_FLAG_ACTIVE_LOW : 0;
+
+ rv = gpiod_line_request_bulk_output_flags(&bulk, consumer,
+ flags, values);
+ if (rv < 0) {
+ gpiod_chip_close(chip);
+ return -1;
+ }
+
+ if (cb)
+ cb(data);
+
+ gpiod_chip_close(chip);
+
+ return 0;
+}
+
+static int basic_event_poll(unsigned int num_lines,
+ struct gpiod_ctxless_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 rv, ret;
+
+ if (!num_lines || num_lines > GPIOD_LINE_BULK_MAX_LINES)
+ return GPIOD_CTXLESS_EVENT_POLL_RET_ERR;
+
+ memset(poll_fds, 0, sizeof(poll_fds));
+
+ for (i = 0; i < num_lines; i++) {
+ poll_fds[i].fd = fds[i].fd;
+ poll_fds[i].events = POLLIN | POLLPRI;
+ }
+
+ rv = ppoll(poll_fds, num_lines, timeout, NULL);
+ if (rv < 0) {
+ if (errno == EINTR)
+ return GPIOD_CTXLESS_EVENT_POLL_RET_TIMEOUT;
+ else
+ return GPIOD_CTXLESS_EVENT_POLL_RET_ERR;
+ } else if (rv == 0) {
+ return GPIOD_CTXLESS_EVENT_POLL_RET_TIMEOUT;
+ }
+
+ ret = rv;
+ for (i = 0; i < num_lines; i++) {
+ if (poll_fds[i].revents) {
+ fds[i].event = true;
+ if (!--rv)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int gpiod_ctxless_event_loop(const char *device, 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(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,
+ 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)
+{
+ 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];
+ 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;
+
+ if (!num_lines || num_lines > GPIOD_LINE_BULK_MAX_LINES) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!poll_cb)
+ poll_cb = basic_event_poll;
+
+ chip = gpiod_chip_open_lookup(device);
+ if (!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);
+ }
+
+ 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(&bulk, &conf, NULL);
+ if (rv) {
+ ret = -1;
+ goto out;
+ }
+
+ memset(fds, 0, sizeof(fds));
+ 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 (;;) {
+ for (i = 0; i < num_lines; i++)
+ fds[i].event = false;
+
+ cnt = poll_cb(num_lines, fds, timeout, data);
+ if (cnt == GPIOD_CTXLESS_EVENT_POLL_RET_ERR) {
+ ret = -1;
+ goto out;
+ } else if (cnt == GPIOD_CTXLESS_EVENT_POLL_RET_TIMEOUT) {
+ rv = event_cb(GPIOD_CTXLESS_EVENT_CB_TIMEOUT,
+ 0, &event.ts, data);
+ if (rv == GPIOD_CTXLESS_EVENT_CB_RET_ERR) {
+ ret = -1;
+ goto out;
+ } else if (rv == GPIOD_CTXLESS_EVENT_CB_RET_STOP) {
+ ret = 0;
+ goto out;
+ }
+ } else if (cnt == GPIOD_CTXLESS_EVENT_POLL_RET_STOP) {
+ ret = 0;
+ goto out;
+ }
+
+ 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_CTXLESS_EVENT_CB_RISING_EDGE;
+ else
+ evtype = GPIOD_CTXLESS_EVENT_CB_FALLING_EDGE;
+
+ rv = event_cb(evtype, gpiod_line_offset(line),
+ &event.ts, data);
+ if (rv == GPIOD_CTXLESS_EVENT_CB_RET_ERR) {
+ ret = -1;
+ goto out;
+ } else if (rv == GPIOD_CTXLESS_EVENT_CB_RET_STOP) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!--cnt)
+ break;
+ }
+ }
+
+out:
+ gpiod_chip_close(chip);
+
+ return ret;
+}
+
+int gpiod_ctxless_find_line(const char *name, char *chipname,
+ size_t chipname_size, unsigned int *offset)
+{
+ struct gpiod_chip *chip;
+ struct gpiod_line *line;
+
+ line = gpiod_line_find(name);
+ if (!line) {
+ if (errno == ENOENT)
+ return 0;
+ else
+ return -1;
+ }
+
+ chip = gpiod_line_get_chip(line);
+ snprintf(chipname, chipname_size, "%s", gpiod_chip_name(chip));
+ *offset = gpiod_line_offset(line);
+ gpiod_chip_close(chip);
+
+ return 1;
+}
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+/*
+ * More specific variants of the core API and misc functions that don't need
+ * access to neither the internal library data structures nor the kernel UAPI.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <gpiod.h>
+#include <stdio.h>
+#include <string.h>
+
+static bool isuint(const char *str)
+{
+ for (; *str && isdigit(*str); str++)
+ ;
+
+ return *str == '\0';
+}
+
+struct gpiod_chip *gpiod_chip_open_by_name(const char *name)
+{
+ struct gpiod_chip *chip;
+ char *path;
+ int rv;
+
+ rv = asprintf(&path, "/dev/%s", name);
+ if (rv < 0)
+ return NULL;
+
+ chip = gpiod_chip_open(path);
+ free(path);
+
+ return chip;
+}
+
+struct gpiod_chip *gpiod_chip_open_by_number(unsigned int num)
+{
+ struct gpiod_chip *chip;
+ char *path;
+ int rv;
+
+ rv = asprintf(&path, "/dev/gpiochip%u", num);
+ if (!rv)
+ return NULL;
+
+ chip = gpiod_chip_open(path);
+ free(path);
+
+ return chip;
+}
+
+struct gpiod_chip *gpiod_chip_open_by_label(const char *label)
+{
+ struct gpiod_chip_iter *iter;
+ struct gpiod_chip *chip;
+
+ iter = gpiod_chip_iter_new();
+ if (!iter)
+ return NULL;
+
+ gpiod_foreach_chip(iter, chip) {
+ if (strcmp(label, gpiod_chip_label(chip)) == 0) {
+ gpiod_chip_iter_free_noclose(iter);
+ return chip;
+ }
+ }
+
+ errno = ENOENT;
+ gpiod_chip_iter_free(iter);
+
+ return NULL;
+}
+
+struct gpiod_chip *gpiod_chip_open_lookup(const char *descr)
+{
+ struct gpiod_chip *chip;
+
+ if (isuint(descr)) {
+ chip = gpiod_chip_open_by_number(strtoul(descr, NULL, 10));
+ } else {
+ chip = gpiod_chip_open_by_label(descr);
+ if (!chip) {
+ if (strncmp(descr, "/dev/", 5))
+ chip = gpiod_chip_open_by_name(descr);
+ else
+ chip = gpiod_chip_open(descr);
+ }
+ }
+
+ return chip;
+}
+
+int gpiod_chip_get_lines(struct gpiod_chip *chip, unsigned int *offsets,
+ unsigned int num_offsets, struct gpiod_line_bulk *bulk)
+{
+ struct gpiod_line *line;
+ unsigned int i;
+
+ gpiod_line_bulk_init(bulk);
+
+ for (i = 0; i < num_offsets; i++) {
+ line = gpiod_chip_get_line(chip, offsets[i]);
+ if (!line)
+ return -1;
+
+ gpiod_line_bulk_add(bulk, line);
+ }
+
+ return 0;
+}
+
+int gpiod_chip_get_all_lines(struct gpiod_chip *chip,
+ struct gpiod_line_bulk *bulk)
+{
+ struct gpiod_line_iter *iter;
+ struct gpiod_line *line;
+
+ gpiod_line_bulk_init(bulk);
+
+ iter = gpiod_line_iter_new(chip);
+ if (!iter)
+ return -1;
+
+ gpiod_foreach_line(iter, line)
+ gpiod_line_bulk_add(bulk, line);
+
+ gpiod_line_iter_free(iter);
+
+ return 0;
+}
+
+struct gpiod_line *
+gpiod_chip_find_line(struct gpiod_chip *chip, const char *name)
+{
+ struct gpiod_line_iter *iter;
+ struct gpiod_line *line;
+ const char *tmp;
+
+ iter = gpiod_line_iter_new(chip);
+ if (!iter)
+ return NULL;
+
+ gpiod_foreach_line(iter, line) {
+ tmp = gpiod_line_name(line);
+ if (tmp && strcmp(tmp, name) == 0) {
+ gpiod_line_iter_free(iter);
+ return line;
+ }
+ }
+
+ errno = ENOENT;
+ gpiod_line_iter_free(iter);
+
+ return NULL;
+}
+
+int gpiod_chip_find_lines(struct gpiod_chip *chip,
+ const char **names, struct gpiod_line_bulk *bulk)
+{
+ struct gpiod_line *line;
+ int i;
+
+ gpiod_line_bulk_init(bulk);
+
+ for (i = 0; names[i]; i++) {
+ line = gpiod_chip_find_line(chip, names[i]);
+ if (!line)
+ return -1;
+
+ gpiod_line_bulk_add(bulk, line);
+ }
+
+ return 0;
+}
+
+int gpiod_line_request_input(struct gpiod_line *line, const char *consumer)
+{
+ struct gpiod_line_request_config config = {
+ .consumer = consumer,
+ .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT,
+ };
+
+ return gpiod_line_request(line, &config, 0);
+}
+
+int gpiod_line_request_output(struct gpiod_line *line,
+ const char *consumer, int default_val)
+{
+ struct gpiod_line_request_config config = {
+ .consumer = consumer,
+ .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
+ };
+
+ return gpiod_line_request(line, &config, default_val);
+}
+
+int gpiod_line_request_input_flags(struct gpiod_line *line,
+ const char *consumer, int flags)
+{
+ struct gpiod_line_request_config config = {
+ .consumer = consumer,
+ .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT,
+ .flags = flags,
+ };
+
+ return gpiod_line_request(line, &config, 0);
+}
+
+int gpiod_line_request_output_flags(struct gpiod_line *line,
+ const char *consumer, int flags,
+ int default_val)
+{
+ struct gpiod_line_request_config config = {
+ .consumer = consumer,
+ .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
+ .flags = flags,
+ };
+
+ return gpiod_line_request(line, &config, default_val);
+}
+
+static int line_event_request_type(struct gpiod_line *line,
+ const char *consumer, int flags, int type)
+{
+ struct gpiod_line_request_config config = {
+ .consumer = consumer,
+ .request_type = type,
+ .flags = flags,
+ };
+
+ return gpiod_line_request(line, &config, 0);
+}
+
+int gpiod_line_request_rising_edge_events(struct gpiod_line *line,
+ const char *consumer)
+{
+ return line_event_request_type(line, consumer, 0,
+ GPIOD_LINE_REQUEST_EVENT_RISING_EDGE);
+}
+
+int gpiod_line_request_falling_edge_events(struct gpiod_line *line,
+ const char *consumer)
+{
+ return line_event_request_type(line, consumer, 0,
+ GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE);
+}
+
+int gpiod_line_request_both_edges_events(struct gpiod_line *line,
+ const char *consumer)
+{
+ return line_event_request_type(line, consumer, 0,
+ GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES);
+}
+
+int gpiod_line_request_rising_edge_events_flags(struct gpiod_line *line,
+ const char *consumer,
+ int flags)
+{
+ return line_event_request_type(line, consumer, flags,
+ GPIOD_LINE_REQUEST_EVENT_RISING_EDGE);
+}
+
+int gpiod_line_request_falling_edge_events_flags(struct gpiod_line *line,
+ const char *consumer,
+ int flags)
+{
+ return line_event_request_type(line, consumer, flags,
+ GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE);
+}
+
+int gpiod_line_request_both_edges_events_flags(struct gpiod_line *line,
+ const char *consumer, int flags)
+{
+ return line_event_request_type(line, consumer, flags,
+ GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES);
+}
+
+int gpiod_line_request_bulk_input(struct gpiod_line_bulk *bulk,
+ const char *consumer)
+{
+ struct gpiod_line_request_config config = {
+ .consumer = consumer,
+ .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT,
+ };
+
+ return gpiod_line_request_bulk(bulk, &config, 0);
+}
+
+int gpiod_line_request_bulk_output(struct gpiod_line_bulk *bulk,
+ const char *consumer,
+ const int *default_vals)
+{
+ struct gpiod_line_request_config config = {
+ .consumer = consumer,
+ .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
+ };
+
+ return gpiod_line_request_bulk(bulk, &config, default_vals);
+}
+
+static int line_event_request_type_bulk(struct gpiod_line_bulk *bulk,
+ const char *consumer,
+ int flags, int type)
+{
+ struct gpiod_line_request_config config = {
+ .consumer = consumer,
+ .request_type = type,
+ .flags = flags,
+ };
+
+ return gpiod_line_request_bulk(bulk, &config, 0);
+}
+
+int gpiod_line_request_bulk_rising_edge_events(struct gpiod_line_bulk *bulk,
+ const char *consumer)
+{
+ return line_event_request_type_bulk(bulk, consumer, 0,
+ GPIOD_LINE_REQUEST_EVENT_RISING_EDGE);
+}
+
+int gpiod_line_request_bulk_falling_edge_events(struct gpiod_line_bulk *bulk,
+ const char *consumer)
+{
+ return line_event_request_type_bulk(bulk, consumer, 0,
+ GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE);
+}
+
+int gpiod_line_request_bulk_both_edges_events(struct gpiod_line_bulk *bulk,
+ const char *consumer)
+{
+ return line_event_request_type_bulk(bulk, consumer, 0,
+ GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES);
+}
+
+int gpiod_line_request_bulk_input_flags(struct gpiod_line_bulk *bulk,
+ const char *consumer, int flags)
+{
+ struct gpiod_line_request_config config = {
+ .consumer = consumer,
+ .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT,
+ .flags = flags,
+ };
+
+ return gpiod_line_request_bulk(bulk, &config, 0);
+}
+
+int gpiod_line_request_bulk_output_flags(struct gpiod_line_bulk *bulk,
+ const char *consumer, int flags,
+ const int *default_vals)
+{
+ struct gpiod_line_request_config config = {
+ .consumer = consumer,
+ .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
+ .flags = flags,
+ };
+
+ return gpiod_line_request_bulk(bulk, &config, default_vals);
+}
+
+int gpiod_line_request_bulk_rising_edge_events_flags(
+ struct gpiod_line_bulk *bulk,
+ const char *consumer, int flags)
+{
+ return line_event_request_type_bulk(bulk, consumer, flags,
+ GPIOD_LINE_REQUEST_EVENT_RISING_EDGE);
+}
+
+int gpiod_line_request_bulk_falling_edge_events_flags(
+ struct gpiod_line_bulk *bulk,
+ const char *consumer, int flags)
+{
+ return line_event_request_type_bulk(bulk, consumer, flags,
+ GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE);
+}
+
+int gpiod_line_request_bulk_both_edges_events_flags(
+ struct gpiod_line_bulk *bulk,
+ const char *consumer, int flags)
+{
+ return line_event_request_type_bulk(bulk, consumer, flags,
+ GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES);
+}
+
+struct gpiod_line *gpiod_line_get(const char *device, unsigned int offset)
+{
+ struct gpiod_chip *chip;
+ struct gpiod_line *line;
+
+ chip = gpiod_chip_open_lookup(device);
+ if (!chip)
+ return NULL;
+
+ line = gpiod_chip_get_line(chip, offset);
+ if (!line) {
+ gpiod_chip_close(chip);
+ return NULL;
+ }
+
+ return line;
+}
+
+struct gpiod_line *gpiod_line_find(const char *name)
+{
+ struct gpiod_chip_iter *iter;
+ struct gpiod_chip *chip;
+ struct gpiod_line *line;
+
+ iter = gpiod_chip_iter_new();
+ if (!iter)
+ return NULL;
+
+ gpiod_foreach_chip(iter, chip) {
+ line = gpiod_chip_find_line(chip, name);
+ if (line) {
+ gpiod_chip_iter_free_noclose(iter);
+ return line;
+ }
+
+ if (errno != ENOENT)
+ goto out;
+ }
+
+ errno = ENOENT;
+
+out:
+ gpiod_chip_iter_free(iter);
+
+ return NULL;
+}
+
+void gpiod_line_close_chip(struct gpiod_line *line)
+{
+ struct gpiod_chip *chip = gpiod_line_get_chip(line);
+
+ gpiod_chip_close(chip);
+}
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+/* GPIO chip and line iterators. */
+
+#include <dirent.h>
+#include <gpiod.h>
+#include <string.h>
+
+struct gpiod_chip_iter {
+ struct gpiod_chip **chips;
+ unsigned int num_chips;
+ unsigned int offset;
+};
+
+struct gpiod_line_iter {
+ struct gpiod_line **lines;
+ unsigned int num_lines;
+ unsigned int offset;
+};
+
+static int dir_filter(const struct dirent *dir)
+{
+ return !strncmp(dir->d_name, "gpiochip", 8);
+}
+
+static void free_dirs(struct dirent ***dirs, unsigned int num_dirs)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_dirs; i++)
+ free((*dirs)[i]);
+ free(*dirs);
+}
+
+struct gpiod_chip_iter *gpiod_chip_iter_new(void)
+{
+ struct gpiod_chip_iter *iter;
+ struct dirent **dirs;
+ int i, num_chips;
+
+ num_chips = scandir("/dev", &dirs, dir_filter, alphasort);
+ if (num_chips < 0)
+ return NULL;
+
+ iter = malloc(sizeof(*iter));
+ if (!iter)
+ goto err_free_dirs;
+
+ iter->num_chips = num_chips;
+ iter->offset = 0;
+
+ if (num_chips == 0) {
+ iter->chips = NULL;
+ return iter;
+ }
+
+ iter->chips = calloc(num_chips, sizeof(*iter->chips));
+ if (!iter->chips)
+ goto err_free_iter;
+
+ for (i = 0; i < num_chips; i++) {
+ iter->chips[i] = gpiod_chip_open_by_name(dirs[i]->d_name);
+ if (!iter->chips[i])
+ goto err_close_chips;
+ }
+
+ free_dirs(&dirs, num_chips);
+
+ return iter;
+
+err_close_chips:
+ for (i = 0; i < num_chips; i++) {
+ if (iter->chips[i])
+ gpiod_chip_close(iter->chips[i]);
+ }
+
+ free(iter->chips);
+
+err_free_iter:
+ free(iter);
+
+err_free_dirs:
+ free_dirs(&dirs, num_chips);
+
+ return NULL;
+}
+
+void gpiod_chip_iter_free(struct gpiod_chip_iter *iter)
+{
+ if (iter->offset > 0 && iter->offset < iter->num_chips)
+ gpiod_chip_close(iter->chips[iter->offset - 1]);
+ gpiod_chip_iter_free_noclose(iter);
+}
+
+void gpiod_chip_iter_free_noclose(struct gpiod_chip_iter *iter)
+{
+ unsigned int i;
+
+ for (i = iter->offset; i < iter->num_chips; i++) {
+ if (iter->chips[i])
+ gpiod_chip_close(iter->chips[i]);
+ }
+
+ if (iter->chips)
+ free(iter->chips);
+
+ free(iter);
+}
+
+struct gpiod_chip *gpiod_chip_iter_next(struct gpiod_chip_iter *iter)
+{
+ if (iter->offset > 0) {
+ gpiod_chip_close(iter->chips[iter->offset - 1]);
+ iter->chips[iter->offset - 1] = NULL;
+ }
+
+ return gpiod_chip_iter_next_noclose(iter);
+}
+
+struct gpiod_chip *gpiod_chip_iter_next_noclose(struct gpiod_chip_iter *iter)
+{
+ return iter->offset < (iter->num_chips)
+ ? iter->chips[iter->offset++] : NULL;
+}
+
+struct gpiod_line_iter *gpiod_line_iter_new(struct gpiod_chip *chip)
+{
+ struct gpiod_line_iter *iter;
+ unsigned int i;
+
+ iter = malloc(sizeof(*iter));
+ if (!iter)
+ return NULL;
+
+ iter->num_lines = gpiod_chip_num_lines(chip);
+ iter->offset = 0;
+
+ iter->lines = calloc(iter->num_lines, sizeof(*iter->lines));
+ if (!iter->lines) {
+ free(iter);
+ return NULL;
+ }
+
+ for (i = 0; i < iter->num_lines; i++) {
+ iter->lines[i] = gpiod_chip_get_line(chip, i);
+ if (!iter->lines[i]) {
+ free(iter->lines);
+ free(iter);
+ return NULL;
+ }
+ }
+
+ return iter;
+}
+
+void gpiod_line_iter_free(struct gpiod_line_iter *iter)
+{
+ free(iter->lines);
+ free(iter);
+}
+
+struct gpiod_line *gpiod_line_iter_next(struct gpiod_line_iter *iter)
+{
+ return iter->offset < (iter->num_lines)
+ ? iter->lines[iter->offset++] : NULL;
+}
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+/* Misc code that didn't fit anywhere else. */
+
+#include <gpiod.h>
+
+const char *gpiod_version_string(void)
+{
+ return GPIOD_VERSION_STR;
+}
dist_man1_MANS = gpiodetect.man gpioinfo.man gpioget.man gpioset.man gpiofind.man gpiomon.man
-%.man: $(top_srcdir)/src/tools/$(*F)
- help2man $(top_srcdir)/src/tools/$(*F) --include=./template --output=./$@ --no-info
+%.man: $(top_srcdir)/tools/$(*F)
+ help2man $(top_srcdir)/tools/$(*F) --include=./template --output=./$@ --no-info
clean-local:
rm -f $(dist_man1_MANS)
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-#
-# This file is part of libgpiod.
-#
-# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
-#
-
-SUBDIRS = lib
-
-if WITH_TOOLS
-
-SUBDIRS += tools
-
-endif
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-#
-# This file is part of libgpiod.
-#
-# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
-#
-
-lib_LTLIBRARIES = libgpiod.la
-libgpiod_la_SOURCES = core.c ctxless.c helpers.c iter.c misc.c
-libgpiod_la_CFLAGS = -Wall -Wextra -g
-libgpiod_la_CFLAGS += -fvisibility=hidden -I$(top_srcdir)/include/
-libgpiod_la_CFLAGS += -include $(top_builddir)/config.h
-libgpiod_la_LDFLAGS = -version-info $(subst .,:,$(ABI_VERSION))
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-/* Low-level, core library code. */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <gpiod.h>
-#include <linux/gpio.h>
-#include <poll.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-enum {
- LINE_FREE = 0,
- LINE_REQUESTED_VALUES,
- LINE_REQUESTED_EVENTS,
-};
-
-struct line_fd_handle {
- int fd;
- int refcount;
-};
-
-struct gpiod_line {
- unsigned int offset;
- int direction;
- int active_state;
- bool used;
- bool open_source;
- bool open_drain;
-
- int state;
- bool up_to_date;
-
- struct gpiod_chip *chip;
- struct line_fd_handle *fd_handle;
-
- char name[32];
- char consumer[32];
-};
-
-struct gpiod_chip {
- struct gpiod_line **lines;
- unsigned int num_lines;
-
- int fd;
-
- char name[32];
- char label[32];
-};
-
-static bool is_gpiochip_cdev(const char *path)
-{
- char *name, *pathcpy, *sysfsp, sysfsdev[16], devstr[16];
- struct stat statbuf;
- bool ret = false;
- int rv, fd;
- ssize_t rd;
-
- rv = lstat(path, &statbuf);
- if (rv)
- goto out;
-
- /* Is it a character device? */
- if (!S_ISCHR(statbuf.st_mode)) {
- /*
- * Passing a file descriptor not associated with a character
- * device to ioctl() makes it set errno to ENOTTY. Let's do
- * the same in order to stay compatible with the versions of
- * libgpiod from before the introduction of this routine.
- */
- errno = ENOTTY;
- goto out;
- }
-
- /* Get the basename. */
- pathcpy = strdup(path);
- if (!pathcpy)
- goto out;
-
- name = basename(pathcpy);
-
- /* Do we have a corresponding sysfs attribute? */
- rv = asprintf(&sysfsp, "/sys/bus/gpio/devices/%s/dev", name);
- if (rv < 0)
- goto out_free_pathcpy;
-
- if (access(sysfsp, R_OK) != 0) {
- /*
- * This is a character device but not the one we're after.
- * Before the introduction of this function, we'd fail with
- * ENOTTY on the first GPIO ioctl() call for this file
- * descriptor. Let's stay compatible here and keep returning
- * the same error code.
- */
- errno = ENOTTY;
- goto out_free_sysfsp;
- }
-
- /*
- * Make sure the major and minor numbers of the character device
- * correspond with the ones in the dev attribute in sysfs.
- */
- snprintf(devstr, sizeof(devstr), "%u:%u",
- major(statbuf.st_rdev), minor(statbuf.st_rdev));
-
- fd = open(sysfsp, O_RDONLY);
- if (fd < 0)
- goto out_free_sysfsp;
-
- memset(sysfsdev, 0, sizeof(sysfsdev));
- rd = read(fd, sysfsdev, strlen(devstr));
- close(fd);
- if (rd < 0)
- goto out_free_sysfsp;
-
- if (strcmp(sysfsdev, devstr) != 0) {
- errno = ENODEV;
- goto out_free_sysfsp;
- }
-
- ret = true;
-
-out_free_sysfsp:
- free(sysfsp);
-out_free_pathcpy:
- free(pathcpy);
-out:
- return ret;
-}
-
-struct gpiod_chip *gpiod_chip_open(const char *path)
-{
- struct gpiochip_info info;
- struct gpiod_chip *chip;
- int rv, fd;
-
- fd = open(path, O_RDWR | O_CLOEXEC);
- if (fd < 0)
- return NULL;
-
- /*
- * We were able to open the file but is it really a gpiochip character
- * device?
- */
- if (!is_gpiochip_cdev(path)) {
- close(fd);
- return NULL;
- }
-
- chip = malloc(sizeof(*chip));
- if (!chip)
- goto err_close_fd;
-
- memset(chip, 0, sizeof(*chip));
- memset(&info, 0, sizeof(info));
-
- rv = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);
- if (rv < 0)
- goto err_free_chip;
-
- chip->fd = fd;
- chip->num_lines = info.lines;
-
- /*
- * GPIO device must have a name - don't bother checking this field. In
- * the worst case (would have to be a weird kernel bug) it'll be empty.
- */
- strncpy(chip->name, info.name, sizeof(chip->name));
-
- /*
- * The kernel sets the label of a GPIO device to "unknown" if it
- * hasn't been defined in DT, board file etc. On the off-chance that
- * we got an empty string, do the same.
- */
- if (info.label[0] == '\0')
- strncpy(chip->label, "unknown", sizeof(chip->label));
- else
- strncpy(chip->label, info.label, sizeof(chip->label));
-
- return chip;
-
-err_free_chip:
- free(chip);
-err_close_fd:
- close(fd);
-
- return NULL;
-}
-
-void gpiod_chip_close(struct gpiod_chip *chip)
-{
- struct gpiod_line *line;
- unsigned int i;
-
- if (chip->lines) {
- for (i = 0; i < chip->num_lines; i++) {
- line = chip->lines[i];
- if (line) {
- gpiod_line_release(line);
- free(line);
- }
- }
-
- free(chip->lines);
- }
-
- close(chip->fd);
- free(chip);
-}
-
-const char *gpiod_chip_name(struct gpiod_chip *chip)
-{
- return chip->name;
-}
-
-const char *gpiod_chip_label(struct gpiod_chip *chip)
-{
- return chip->label;
-}
-
-unsigned int gpiod_chip_num_lines(struct gpiod_chip *chip)
-{
- return chip->num_lines;
-}
-
-struct gpiod_line *
-gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset)
-{
- struct gpiod_line *line;
- int rv;
-
- if (offset >= chip->num_lines) {
- errno = EINVAL;
- return NULL;
- }
-
- if (!chip->lines) {
- chip->lines = calloc(chip->num_lines,
- sizeof(struct gpiod_line *));
- if (!chip->lines)
- return NULL;
- }
-
- if (!chip->lines[offset]) {
- line = malloc(sizeof(*line));
- if (!line)
- return NULL;
-
- memset(line, 0, sizeof(*line));
-
- line->offset = offset;
- line->chip = chip;
-
- chip->lines[offset] = line;
- } else {
- line = chip->lines[offset];
- }
-
- rv = gpiod_line_update(line);
- if (rv < 0)
- return NULL;
-
- return line;
-}
-
-static struct line_fd_handle *line_make_fd_handle(int fd)
-{
- struct line_fd_handle *handle;
-
- handle = malloc(sizeof(*handle));
- if (!handle)
- return NULL;
-
- handle->fd = fd;
- handle->refcount = 0;
-
- return handle;
-}
-
-static void line_fd_incref(struct gpiod_line *line)
-{
- line->fd_handle->refcount++;
-}
-
-static void line_fd_decref(struct gpiod_line *line)
-{
- struct line_fd_handle *handle = line->fd_handle;
-
- handle->refcount--;
-
- if (handle->refcount == 0) {
- close(handle->fd);
- free(handle);
- line->fd_handle = NULL;
- }
-}
-
-static void line_set_fd(struct gpiod_line *line, struct line_fd_handle *handle)
-{
- line->fd_handle = handle;
- line_fd_incref(line);
-}
-
-static int line_get_fd(struct gpiod_line *line)
-{
- return line->fd_handle->fd;
-}
-
-static void line_maybe_update(struct gpiod_line *line)
-{
- int rv;
-
- rv = gpiod_line_update(line);
- if (rv < 0)
- line->up_to_date = false;
-}
-
-struct gpiod_chip *gpiod_line_get_chip(struct gpiod_line *line)
-{
- return line->chip;
-}
-
-unsigned int gpiod_line_offset(struct gpiod_line *line)
-{
- return line->offset;
-}
-
-const char *gpiod_line_name(struct gpiod_line *line)
-{
- return line->name[0] == '\0' ? NULL : line->name;
-}
-
-const char *gpiod_line_consumer(struct gpiod_line *line)
-{
- return line->consumer[0] == '\0' ? NULL : line->consumer;
-}
-
-int gpiod_line_direction(struct gpiod_line *line)
-{
- return line->direction;
-}
-
-int gpiod_line_active_state(struct gpiod_line *line)
-{
- return line->active_state;
-}
-
-bool gpiod_line_is_used(struct gpiod_line *line)
-{
- return line->used;
-}
-
-bool gpiod_line_is_open_drain(struct gpiod_line *line)
-{
- return line->open_drain;
-}
-
-bool gpiod_line_is_open_source(struct gpiod_line *line)
-{
- return line->open_source;
-}
-
-bool gpiod_line_needs_update(struct gpiod_line *line)
-{
- return !line->up_to_date;
-}
-
-int gpiod_line_update(struct gpiod_line *line)
-{
- struct gpioline_info info;
- int rv;
-
- memset(&info, 0, sizeof(info));
- info.line_offset = line->offset;
-
- rv = ioctl(line->chip->fd, GPIO_GET_LINEINFO_IOCTL, &info);
- if (rv < 0)
- return -1;
-
- line->direction = info.flags & GPIOLINE_FLAG_IS_OUT
- ? GPIOD_LINE_DIRECTION_OUTPUT
- : GPIOD_LINE_DIRECTION_INPUT;
- line->active_state = info.flags & GPIOLINE_FLAG_ACTIVE_LOW
- ? GPIOD_LINE_ACTIVE_STATE_LOW
- : GPIOD_LINE_ACTIVE_STATE_HIGH;
-
- line->used = info.flags & GPIOLINE_FLAG_KERNEL;
- line->open_drain = info.flags & GPIOLINE_FLAG_OPEN_DRAIN;
- line->open_source = info.flags & GPIOLINE_FLAG_OPEN_SOURCE;
-
- strncpy(line->name, info.name, sizeof(line->name));
- strncpy(line->consumer, info.consumer, sizeof(line->consumer));
-
- line->up_to_date = true;
-
- return 0;
-}
-
-static bool line_bulk_same_chip(struct gpiod_line_bulk *bulk)
-{
- struct gpiod_line *first_line, *line;
- struct gpiod_chip *first_chip, *chip;
- unsigned int i;
-
- if (bulk->num_lines == 1)
- return true;
-
- first_line = gpiod_line_bulk_get_line(bulk, 0);
- first_chip = gpiod_line_get_chip(first_line);
-
- for (i = 1; i < bulk->num_lines; i++) {
- line = bulk->lines[i];
- chip = gpiod_line_get_chip(line);
-
- if (first_chip != chip) {
- errno = EINVAL;
- return false;
- }
- }
-
- return true;
-}
-
-static bool line_bulk_all_requested(struct gpiod_line_bulk *bulk)
-{
- struct gpiod_line *line, **lineptr;
-
- gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
- if (!gpiod_line_is_requested(line)) {
- errno = EPERM;
- return false;
- }
- }
-
- return true;
-}
-
-static bool line_bulk_all_free(struct gpiod_line_bulk *bulk)
-{
- struct gpiod_line *line, **lineptr;
-
- gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
- if (!gpiod_line_is_free(line)) {
- errno = EBUSY;
- return false;
- }
- }
-
- return true;
-}
-
-static int line_request_values(struct gpiod_line_bulk *bulk,
- const struct gpiod_line_request_config *config,
- const int *default_vals)
-{
- struct gpiod_line *line, **lineptr;
- struct line_fd_handle *line_fd;
- struct gpiohandle_request req;
- unsigned int i;
- int rv, fd;
-
- if ((config->request_type != GPIOD_LINE_REQUEST_DIRECTION_OUTPUT) &&
- (config->flags & (GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN |
- GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE))) {
- errno = EINVAL;
- return -1;
- }
-
- if ((config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN) &&
- (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)) {
- errno = EINVAL;
- return -1;
- }
-
- memset(&req, 0, sizeof(req));
-
- if (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN)
- req.flags |= GPIOHANDLE_REQUEST_OPEN_DRAIN;
- if (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)
- req.flags |= GPIOHANDLE_REQUEST_OPEN_SOURCE;
- if (config->flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW)
- req.flags |= GPIOHANDLE_REQUEST_ACTIVE_LOW;
-
- if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_INPUT)
- req.flags |= GPIOHANDLE_REQUEST_INPUT;
- else if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
- req.flags |= GPIOHANDLE_REQUEST_OUTPUT;
-
- req.lines = gpiod_line_bulk_num_lines(bulk);
-
- gpiod_line_bulk_foreach_line_off(bulk, line, i) {
- req.lineoffsets[i] = gpiod_line_offset(line);
- if (config->request_type ==
- GPIOD_LINE_REQUEST_DIRECTION_OUTPUT &&
- default_vals)
- req.default_values[i] = !!default_vals[i];
- }
-
- if (config->consumer)
- strncpy(req.consumer_label, config->consumer,
- sizeof(req.consumer_label) - 1);
-
- line = gpiod_line_bulk_get_line(bulk, 0);
- fd = line->chip->fd;
-
- rv = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
- if (rv < 0)
- return -1;
-
- line_fd = line_make_fd_handle(req.fd);
- if (!line_fd)
- return -1;
-
- gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
- line->state = LINE_REQUESTED_VALUES;
- line_set_fd(line, line_fd);
- line_maybe_update(line);
- }
-
- return 0;
-}
-
-static int line_request_event_single(struct gpiod_line *line,
- const struct gpiod_line_request_config *config)
-{
- struct line_fd_handle *line_fd;
- struct gpioevent_request req;
- int rv;
-
- memset(&req, 0, sizeof(req));
-
- if (config->consumer)
- strncpy(req.consumer_label, config->consumer,
- sizeof(req.consumer_label) - 1);
-
- req.lineoffset = gpiod_line_offset(line);
- req.handleflags |= GPIOHANDLE_REQUEST_INPUT;
-
- if (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN)
- req.handleflags |= GPIOHANDLE_REQUEST_OPEN_DRAIN;
- if (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)
- req.handleflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE;
- if (config->flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW)
- req.handleflags |= GPIOHANDLE_REQUEST_ACTIVE_LOW;
-
- if (config->request_type == GPIOD_LINE_REQUEST_EVENT_RISING_EDGE)
- req.eventflags |= GPIOEVENT_REQUEST_RISING_EDGE;
- else if (config->request_type == GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE)
- req.eventflags |= GPIOEVENT_REQUEST_FALLING_EDGE;
- else if (config->request_type == GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES)
- req.eventflags |= GPIOEVENT_REQUEST_BOTH_EDGES;
-
- rv = ioctl(line->chip->fd, GPIO_GET_LINEEVENT_IOCTL, &req);
- if (rv < 0)
- return -1;
-
- line_fd = line_make_fd_handle(req.fd);
- if (!line_fd)
- return -1;
-
- line->state = LINE_REQUESTED_EVENTS;
- line_set_fd(line, line_fd);
- line_maybe_update(line);
-
- return 0;
-}
-
-static int line_request_events(struct gpiod_line_bulk *bulk,
- const struct gpiod_line_request_config *config)
-{
- struct gpiod_line *line;
- unsigned int off;
- int rv, rev;
-
- gpiod_line_bulk_foreach_line_off(bulk, line, off) {
- rv = line_request_event_single(line, config);
- if (rv) {
- for (rev = off - 1; rev >= 0; rev--) {
- line = gpiod_line_bulk_get_line(bulk, rev);
- gpiod_line_release(line);
- }
-
- return -1;
- }
- }
-
- return 0;
-}
-
-int gpiod_line_request(struct gpiod_line *line,
- const struct gpiod_line_request_config *config,
- int default_val)
-{
- struct gpiod_line_bulk bulk;
-
- gpiod_line_bulk_init(&bulk);
- gpiod_line_bulk_add(&bulk, line);
-
- return gpiod_line_request_bulk(&bulk, config, &default_val);
-}
-
-static bool line_request_is_direction(int request)
-{
- return request == GPIOD_LINE_REQUEST_DIRECTION_AS_IS ||
- request == GPIOD_LINE_REQUEST_DIRECTION_INPUT ||
- request == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT;
-}
-
-static bool line_request_is_events(int request)
-{
- return request == GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE ||
- request == GPIOD_LINE_REQUEST_EVENT_RISING_EDGE ||
- request == GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES;
-}
-
-int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk,
- const struct gpiod_line_request_config *config,
- const int *default_vals)
-{
- if (!line_bulk_same_chip(bulk) || !line_bulk_all_free(bulk))
- return -1;
-
- if (line_request_is_direction(config->request_type))
- return line_request_values(bulk, config, default_vals);
- else if (line_request_is_events(config->request_type))
- return line_request_events(bulk, config);
-
- errno = EINVAL;
- return -1;
-}
-
-void gpiod_line_release(struct gpiod_line *line)
-{
- struct gpiod_line_bulk bulk;
-
- gpiod_line_bulk_init(&bulk);
- gpiod_line_bulk_add(&bulk, line);
-
- gpiod_line_release_bulk(&bulk);
-}
-
-void gpiod_line_release_bulk(struct gpiod_line_bulk *bulk)
-{
- struct gpiod_line *line, **lineptr;
-
- gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
- if (line->state != LINE_FREE) {
- line_fd_decref(line);
- line->state = LINE_FREE;
- }
- }
-}
-
-bool gpiod_line_is_requested(struct gpiod_line *line)
-{
- return (line->state == LINE_REQUESTED_VALUES ||
- line->state == LINE_REQUESTED_EVENTS);
-}
-
-bool gpiod_line_is_free(struct gpiod_line *line)
-{
- return line->state == LINE_FREE;
-}
-
-int gpiod_line_get_value(struct gpiod_line *line)
-{
- struct gpiod_line_bulk bulk;
- int rv, value;
-
- gpiod_line_bulk_init(&bulk);
- gpiod_line_bulk_add(&bulk, line);
-
- rv = gpiod_line_get_value_bulk(&bulk, &value);
- if (rv < 0)
- return -1;
-
- return value;
-}
-
-int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, int *values)
-{
- struct gpiohandle_data data;
- struct gpiod_line *first;
- unsigned int i;
- int rv, fd;
-
- if (!line_bulk_same_chip(bulk) || !line_bulk_all_requested(bulk))
- return -1;
-
- first = gpiod_line_bulk_get_line(bulk, 0);
-
- memset(&data, 0, sizeof(data));
-
- fd = line_get_fd(first);
-
- rv = ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
- if (rv < 0)
- return -1;
-
- for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++)
- values[i] = data.values[i];
-
- return 0;
-}
-
-int gpiod_line_set_value(struct gpiod_line *line, int value)
-{
- struct gpiod_line_bulk bulk;
-
- gpiod_line_bulk_init(&bulk);
- gpiod_line_bulk_add(&bulk, line);
-
- return gpiod_line_set_value_bulk(&bulk, &value);
-}
-
-int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, const int *values)
-{
- struct gpiohandle_data data;
- struct gpiod_line *line;
- unsigned int i;
- int rv, fd;
-
- if (!line_bulk_same_chip(bulk) || !line_bulk_all_requested(bulk))
- return -1;
-
- memset(&data, 0, sizeof(data));
-
- for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++)
- data.values[i] = (uint8_t)!!values[i];
-
- line = gpiod_line_bulk_get_line(bulk, 0);
- fd = line_get_fd(line);
-
- rv = ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
- if (rv < 0)
- return -1;
-
- return 0;
-}
-
-int gpiod_line_event_wait(struct gpiod_line *line,
- const struct timespec *timeout)
-{
- struct gpiod_line_bulk bulk;
-
- gpiod_line_bulk_init(&bulk);
- gpiod_line_bulk_add(&bulk, line);
-
- return gpiod_line_event_wait_bulk(&bulk, timeout, NULL);
-}
-
-int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk,
- const struct timespec *timeout,
- struct gpiod_line_bulk *event_bulk)
-{
- struct pollfd fds[GPIOD_LINE_BULK_MAX_LINES];
- unsigned int off, num_lines;
- struct gpiod_line *line;
- int rv;
-
- if (!line_bulk_same_chip(bulk) || !line_bulk_all_requested(bulk))
- return -1;
-
- memset(fds, 0, sizeof(fds));
- num_lines = gpiod_line_bulk_num_lines(bulk);
-
- gpiod_line_bulk_foreach_line_off(bulk, line, off) {
- fds[off].fd = line_get_fd(line);
- fds[off].events = POLLIN | POLLPRI;
- }
-
- rv = ppoll(fds, num_lines, timeout, NULL);
- if (rv < 0)
- return -1;
- else if (rv == 0)
- return 0;
-
- if (event_bulk)
- gpiod_line_bulk_init(event_bulk);
-
- for (off = 0; off < num_lines; off++) {
- if (fds[off].revents) {
- if (fds[off].revents & POLLNVAL) {
- errno = EINVAL;
- return -1;
- }
-
- if (event_bulk) {
- line = gpiod_line_bulk_get_line(bulk, off);
- gpiod_line_bulk_add(event_bulk, line);
- }
-
- if (!--rv)
- break;
- }
- }
-
- return 1;
-}
-
-int gpiod_line_event_read(struct gpiod_line *line,
- struct gpiod_line_event *event)
-{
- int fd;
-
- if (line->state != LINE_REQUESTED_EVENTS) {
- errno = EPERM;
- return -1;
- }
-
- fd = line_get_fd(line);
-
- return gpiod_line_event_read_fd(fd, event);
-}
-
-int gpiod_line_event_get_fd(struct gpiod_line *line)
-{
- if (line->state != LINE_REQUESTED_EVENTS) {
- errno = EPERM;
- return -1;
- }
-
- return line_get_fd(line);
-}
-
-int gpiod_line_event_read_fd(int fd, struct gpiod_line_event *event)
-{
- struct gpioevent_data evdata;
- ssize_t rd;
-
- memset(&evdata, 0, sizeof(evdata));
-
- rd = read(fd, &evdata, sizeof(evdata));
- if (rd < 0) {
- return -1;
- } else if (rd != sizeof(evdata)) {
- errno = EIO;
- return -1;
- }
-
- event->event_type = evdata.id == GPIOEVENT_EVENT_RISING_EDGE
- ? GPIOD_LINE_EVENT_RISING_EDGE
- : GPIOD_LINE_EVENT_FALLING_EDGE;
-
- event->ts.tv_sec = evdata.timestamp / 1000000000ULL;
- event->ts.tv_nsec = evdata.timestamp % 1000000000ULL;
-
- return 0;
-}
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-/* Implementation of the high-level API. */
-
-
-#include <errno.h>
-#include <gpiod.h>
-#include <poll.h>
-#include <stdio.h>
-#include <string.h>
-
-int gpiod_ctxless_get_value(const char *device, unsigned int offset,
- bool active_low, const char *consumer)
-{
- int value, rv;
-
- rv = gpiod_ctxless_get_value_multiple(device, &offset, &value,
- 1, active_low, consumer);
- if (rv < 0)
- return rv;
-
- return value;
-}
-
-int gpiod_ctxless_get_value_multiple(const char *device,
- const unsigned int *offsets, int *values,
- unsigned int num_lines, bool active_low,
- const char *consumer)
-{
- struct gpiod_line_bulk bulk;
- struct gpiod_chip *chip;
- struct gpiod_line *line;
- unsigned int i;
- int rv, flags;
-
- if (!num_lines || num_lines > GPIOD_LINE_BULK_MAX_LINES) {
- errno = EINVAL;
- return -1;
- }
-
- chip = gpiod_chip_open_lookup(device);
- if (!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_LINE_REQUEST_FLAG_ACTIVE_LOW : 0;
-
- rv = gpiod_line_request_bulk_input_flags(&bulk, consumer, flags);
- if (rv < 0) {
- gpiod_chip_close(chip);
- return -1;
- }
-
- memset(values, 0, sizeof(*values) * num_lines);
- rv = gpiod_line_get_value_bulk(&bulk, values);
-
- gpiod_chip_close(chip);
-
- return rv;
-}
-
-int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value,
- bool active_low, const char *consumer,
- gpiod_ctxless_set_value_cb cb, void *data)
-{
- return gpiod_ctxless_set_value_multiple(device, &offset, &value, 1,
- active_low, consumer, cb, data);
-}
-
-int gpiod_ctxless_set_value_multiple(const char *device,
- const unsigned int *offsets,
- const int *values, unsigned int num_lines,
- bool active_low, const char *consumer,
- gpiod_ctxless_set_value_cb cb, void *data)
-{
- struct gpiod_line_bulk bulk;
- struct gpiod_chip *chip;
- struct gpiod_line *line;
- unsigned int i;
- int rv, flags;
-
- if (!num_lines || num_lines > GPIOD_LINE_BULK_MAX_LINES) {
- errno = EINVAL;
- return -1;
- }
-
- chip = gpiod_chip_open_lookup(device);
- if (!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_LINE_REQUEST_FLAG_ACTIVE_LOW : 0;
-
- rv = gpiod_line_request_bulk_output_flags(&bulk, consumer,
- flags, values);
- if (rv < 0) {
- gpiod_chip_close(chip);
- return -1;
- }
-
- if (cb)
- cb(data);
-
- gpiod_chip_close(chip);
-
- return 0;
-}
-
-static int basic_event_poll(unsigned int num_lines,
- struct gpiod_ctxless_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 rv, ret;
-
- if (!num_lines || num_lines > GPIOD_LINE_BULK_MAX_LINES)
- return GPIOD_CTXLESS_EVENT_POLL_RET_ERR;
-
- memset(poll_fds, 0, sizeof(poll_fds));
-
- for (i = 0; i < num_lines; i++) {
- poll_fds[i].fd = fds[i].fd;
- poll_fds[i].events = POLLIN | POLLPRI;
- }
-
- rv = ppoll(poll_fds, num_lines, timeout, NULL);
- if (rv < 0) {
- if (errno == EINTR)
- return GPIOD_CTXLESS_EVENT_POLL_RET_TIMEOUT;
- else
- return GPIOD_CTXLESS_EVENT_POLL_RET_ERR;
- } else if (rv == 0) {
- return GPIOD_CTXLESS_EVENT_POLL_RET_TIMEOUT;
- }
-
- ret = rv;
- for (i = 0; i < num_lines; i++) {
- if (poll_fds[i].revents) {
- fds[i].event = true;
- if (!--rv)
- break;
- }
- }
-
- return ret;
-}
-
-int gpiod_ctxless_event_loop(const char *device, 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(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,
- 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)
-{
- 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];
- 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;
-
- if (!num_lines || num_lines > GPIOD_LINE_BULK_MAX_LINES) {
- errno = EINVAL;
- return -1;
- }
-
- if (!poll_cb)
- poll_cb = basic_event_poll;
-
- chip = gpiod_chip_open_lookup(device);
- if (!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);
- }
-
- 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(&bulk, &conf, NULL);
- if (rv) {
- ret = -1;
- goto out;
- }
-
- memset(fds, 0, sizeof(fds));
- 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 (;;) {
- for (i = 0; i < num_lines; i++)
- fds[i].event = false;
-
- cnt = poll_cb(num_lines, fds, timeout, data);
- if (cnt == GPIOD_CTXLESS_EVENT_POLL_RET_ERR) {
- ret = -1;
- goto out;
- } else if (cnt == GPIOD_CTXLESS_EVENT_POLL_RET_TIMEOUT) {
- rv = event_cb(GPIOD_CTXLESS_EVENT_CB_TIMEOUT,
- 0, &event.ts, data);
- if (rv == GPIOD_CTXLESS_EVENT_CB_RET_ERR) {
- ret = -1;
- goto out;
- } else if (rv == GPIOD_CTXLESS_EVENT_CB_RET_STOP) {
- ret = 0;
- goto out;
- }
- } else if (cnt == GPIOD_CTXLESS_EVENT_POLL_RET_STOP) {
- ret = 0;
- goto out;
- }
-
- 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_CTXLESS_EVENT_CB_RISING_EDGE;
- else
- evtype = GPIOD_CTXLESS_EVENT_CB_FALLING_EDGE;
-
- rv = event_cb(evtype, gpiod_line_offset(line),
- &event.ts, data);
- if (rv == GPIOD_CTXLESS_EVENT_CB_RET_ERR) {
- ret = -1;
- goto out;
- } else if (rv == GPIOD_CTXLESS_EVENT_CB_RET_STOP) {
- ret = 0;
- goto out;
- }
-
- if (!--cnt)
- break;
- }
- }
-
-out:
- gpiod_chip_close(chip);
-
- return ret;
-}
-
-int gpiod_ctxless_find_line(const char *name, char *chipname,
- size_t chipname_size, unsigned int *offset)
-{
- struct gpiod_chip *chip;
- struct gpiod_line *line;
-
- line = gpiod_line_find(name);
- if (!line) {
- if (errno == ENOENT)
- return 0;
- else
- return -1;
- }
-
- chip = gpiod_line_get_chip(line);
- snprintf(chipname, chipname_size, "%s", gpiod_chip_name(chip));
- *offset = gpiod_line_offset(line);
- gpiod_chip_close(chip);
-
- return 1;
-}
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-/*
- * More specific variants of the core API and misc functions that don't need
- * access to neither the internal library data structures nor the kernel UAPI.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <gpiod.h>
-#include <stdio.h>
-#include <string.h>
-
-static bool isuint(const char *str)
-{
- for (; *str && isdigit(*str); str++)
- ;
-
- return *str == '\0';
-}
-
-struct gpiod_chip *gpiod_chip_open_by_name(const char *name)
-{
- struct gpiod_chip *chip;
- char *path;
- int rv;
-
- rv = asprintf(&path, "/dev/%s", name);
- if (rv < 0)
- return NULL;
-
- chip = gpiod_chip_open(path);
- free(path);
-
- return chip;
-}
-
-struct gpiod_chip *gpiod_chip_open_by_number(unsigned int num)
-{
- struct gpiod_chip *chip;
- char *path;
- int rv;
-
- rv = asprintf(&path, "/dev/gpiochip%u", num);
- if (!rv)
- return NULL;
-
- chip = gpiod_chip_open(path);
- free(path);
-
- return chip;
-}
-
-struct gpiod_chip *gpiod_chip_open_by_label(const char *label)
-{
- struct gpiod_chip_iter *iter;
- struct gpiod_chip *chip;
-
- iter = gpiod_chip_iter_new();
- if (!iter)
- return NULL;
-
- gpiod_foreach_chip(iter, chip) {
- if (strcmp(label, gpiod_chip_label(chip)) == 0) {
- gpiod_chip_iter_free_noclose(iter);
- return chip;
- }
- }
-
- errno = ENOENT;
- gpiod_chip_iter_free(iter);
-
- return NULL;
-}
-
-struct gpiod_chip *gpiod_chip_open_lookup(const char *descr)
-{
- struct gpiod_chip *chip;
-
- if (isuint(descr)) {
- chip = gpiod_chip_open_by_number(strtoul(descr, NULL, 10));
- } else {
- chip = gpiod_chip_open_by_label(descr);
- if (!chip) {
- if (strncmp(descr, "/dev/", 5))
- chip = gpiod_chip_open_by_name(descr);
- else
- chip = gpiod_chip_open(descr);
- }
- }
-
- return chip;
-}
-
-int gpiod_chip_get_lines(struct gpiod_chip *chip, unsigned int *offsets,
- unsigned int num_offsets, struct gpiod_line_bulk *bulk)
-{
- struct gpiod_line *line;
- unsigned int i;
-
- gpiod_line_bulk_init(bulk);
-
- for (i = 0; i < num_offsets; i++) {
- line = gpiod_chip_get_line(chip, offsets[i]);
- if (!line)
- return -1;
-
- gpiod_line_bulk_add(bulk, line);
- }
-
- return 0;
-}
-
-int gpiod_chip_get_all_lines(struct gpiod_chip *chip,
- struct gpiod_line_bulk *bulk)
-{
- struct gpiod_line_iter *iter;
- struct gpiod_line *line;
-
- gpiod_line_bulk_init(bulk);
-
- iter = gpiod_line_iter_new(chip);
- if (!iter)
- return -1;
-
- gpiod_foreach_line(iter, line)
- gpiod_line_bulk_add(bulk, line);
-
- gpiod_line_iter_free(iter);
-
- return 0;
-}
-
-struct gpiod_line *
-gpiod_chip_find_line(struct gpiod_chip *chip, const char *name)
-{
- struct gpiod_line_iter *iter;
- struct gpiod_line *line;
- const char *tmp;
-
- iter = gpiod_line_iter_new(chip);
- if (!iter)
- return NULL;
-
- gpiod_foreach_line(iter, line) {
- tmp = gpiod_line_name(line);
- if (tmp && strcmp(tmp, name) == 0) {
- gpiod_line_iter_free(iter);
- return line;
- }
- }
-
- errno = ENOENT;
- gpiod_line_iter_free(iter);
-
- return NULL;
-}
-
-int gpiod_chip_find_lines(struct gpiod_chip *chip,
- const char **names, struct gpiod_line_bulk *bulk)
-{
- struct gpiod_line *line;
- int i;
-
- gpiod_line_bulk_init(bulk);
-
- for (i = 0; names[i]; i++) {
- line = gpiod_chip_find_line(chip, names[i]);
- if (!line)
- return -1;
-
- gpiod_line_bulk_add(bulk, line);
- }
-
- return 0;
-}
-
-int gpiod_line_request_input(struct gpiod_line *line, const char *consumer)
-{
- struct gpiod_line_request_config config = {
- .consumer = consumer,
- .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT,
- };
-
- return gpiod_line_request(line, &config, 0);
-}
-
-int gpiod_line_request_output(struct gpiod_line *line,
- const char *consumer, int default_val)
-{
- struct gpiod_line_request_config config = {
- .consumer = consumer,
- .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
- };
-
- return gpiod_line_request(line, &config, default_val);
-}
-
-int gpiod_line_request_input_flags(struct gpiod_line *line,
- const char *consumer, int flags)
-{
- struct gpiod_line_request_config config = {
- .consumer = consumer,
- .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT,
- .flags = flags,
- };
-
- return gpiod_line_request(line, &config, 0);
-}
-
-int gpiod_line_request_output_flags(struct gpiod_line *line,
- const char *consumer, int flags,
- int default_val)
-{
- struct gpiod_line_request_config config = {
- .consumer = consumer,
- .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
- .flags = flags,
- };
-
- return gpiod_line_request(line, &config, default_val);
-}
-
-static int line_event_request_type(struct gpiod_line *line,
- const char *consumer, int flags, int type)
-{
- struct gpiod_line_request_config config = {
- .consumer = consumer,
- .request_type = type,
- .flags = flags,
- };
-
- return gpiod_line_request(line, &config, 0);
-}
-
-int gpiod_line_request_rising_edge_events(struct gpiod_line *line,
- const char *consumer)
-{
- return line_event_request_type(line, consumer, 0,
- GPIOD_LINE_REQUEST_EVENT_RISING_EDGE);
-}
-
-int gpiod_line_request_falling_edge_events(struct gpiod_line *line,
- const char *consumer)
-{
- return line_event_request_type(line, consumer, 0,
- GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE);
-}
-
-int gpiod_line_request_both_edges_events(struct gpiod_line *line,
- const char *consumer)
-{
- return line_event_request_type(line, consumer, 0,
- GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES);
-}
-
-int gpiod_line_request_rising_edge_events_flags(struct gpiod_line *line,
- const char *consumer,
- int flags)
-{
- return line_event_request_type(line, consumer, flags,
- GPIOD_LINE_REQUEST_EVENT_RISING_EDGE);
-}
-
-int gpiod_line_request_falling_edge_events_flags(struct gpiod_line *line,
- const char *consumer,
- int flags)
-{
- return line_event_request_type(line, consumer, flags,
- GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE);
-}
-
-int gpiod_line_request_both_edges_events_flags(struct gpiod_line *line,
- const char *consumer, int flags)
-{
- return line_event_request_type(line, consumer, flags,
- GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES);
-}
-
-int gpiod_line_request_bulk_input(struct gpiod_line_bulk *bulk,
- const char *consumer)
-{
- struct gpiod_line_request_config config = {
- .consumer = consumer,
- .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT,
- };
-
- return gpiod_line_request_bulk(bulk, &config, 0);
-}
-
-int gpiod_line_request_bulk_output(struct gpiod_line_bulk *bulk,
- const char *consumer,
- const int *default_vals)
-{
- struct gpiod_line_request_config config = {
- .consumer = consumer,
- .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
- };
-
- return gpiod_line_request_bulk(bulk, &config, default_vals);
-}
-
-static int line_event_request_type_bulk(struct gpiod_line_bulk *bulk,
- const char *consumer,
- int flags, int type)
-{
- struct gpiod_line_request_config config = {
- .consumer = consumer,
- .request_type = type,
- .flags = flags,
- };
-
- return gpiod_line_request_bulk(bulk, &config, 0);
-}
-
-int gpiod_line_request_bulk_rising_edge_events(struct gpiod_line_bulk *bulk,
- const char *consumer)
-{
- return line_event_request_type_bulk(bulk, consumer, 0,
- GPIOD_LINE_REQUEST_EVENT_RISING_EDGE);
-}
-
-int gpiod_line_request_bulk_falling_edge_events(struct gpiod_line_bulk *bulk,
- const char *consumer)
-{
- return line_event_request_type_bulk(bulk, consumer, 0,
- GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE);
-}
-
-int gpiod_line_request_bulk_both_edges_events(struct gpiod_line_bulk *bulk,
- const char *consumer)
-{
- return line_event_request_type_bulk(bulk, consumer, 0,
- GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES);
-}
-
-int gpiod_line_request_bulk_input_flags(struct gpiod_line_bulk *bulk,
- const char *consumer, int flags)
-{
- struct gpiod_line_request_config config = {
- .consumer = consumer,
- .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT,
- .flags = flags,
- };
-
- return gpiod_line_request_bulk(bulk, &config, 0);
-}
-
-int gpiod_line_request_bulk_output_flags(struct gpiod_line_bulk *bulk,
- const char *consumer, int flags,
- const int *default_vals)
-{
- struct gpiod_line_request_config config = {
- .consumer = consumer,
- .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
- .flags = flags,
- };
-
- return gpiod_line_request_bulk(bulk, &config, default_vals);
-}
-
-int gpiod_line_request_bulk_rising_edge_events_flags(
- struct gpiod_line_bulk *bulk,
- const char *consumer, int flags)
-{
- return line_event_request_type_bulk(bulk, consumer, flags,
- GPIOD_LINE_REQUEST_EVENT_RISING_EDGE);
-}
-
-int gpiod_line_request_bulk_falling_edge_events_flags(
- struct gpiod_line_bulk *bulk,
- const char *consumer, int flags)
-{
- return line_event_request_type_bulk(bulk, consumer, flags,
- GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE);
-}
-
-int gpiod_line_request_bulk_both_edges_events_flags(
- struct gpiod_line_bulk *bulk,
- const char *consumer, int flags)
-{
- return line_event_request_type_bulk(bulk, consumer, flags,
- GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES);
-}
-
-struct gpiod_line *gpiod_line_get(const char *device, unsigned int offset)
-{
- struct gpiod_chip *chip;
- struct gpiod_line *line;
-
- chip = gpiod_chip_open_lookup(device);
- if (!chip)
- return NULL;
-
- line = gpiod_chip_get_line(chip, offset);
- if (!line) {
- gpiod_chip_close(chip);
- return NULL;
- }
-
- return line;
-}
-
-struct gpiod_line *gpiod_line_find(const char *name)
-{
- struct gpiod_chip_iter *iter;
- struct gpiod_chip *chip;
- struct gpiod_line *line;
-
- iter = gpiod_chip_iter_new();
- if (!iter)
- return NULL;
-
- gpiod_foreach_chip(iter, chip) {
- line = gpiod_chip_find_line(chip, name);
- if (line) {
- gpiod_chip_iter_free_noclose(iter);
- return line;
- }
-
- if (errno != ENOENT)
- goto out;
- }
-
- errno = ENOENT;
-
-out:
- gpiod_chip_iter_free(iter);
-
- return NULL;
-}
-
-void gpiod_line_close_chip(struct gpiod_line *line)
-{
- struct gpiod_chip *chip = gpiod_line_get_chip(line);
-
- gpiod_chip_close(chip);
-}
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-/* GPIO chip and line iterators. */
-
-#include <dirent.h>
-#include <gpiod.h>
-#include <string.h>
-
-struct gpiod_chip_iter {
- struct gpiod_chip **chips;
- unsigned int num_chips;
- unsigned int offset;
-};
-
-struct gpiod_line_iter {
- struct gpiod_line **lines;
- unsigned int num_lines;
- unsigned int offset;
-};
-
-static int dir_filter(const struct dirent *dir)
-{
- return !strncmp(dir->d_name, "gpiochip", 8);
-}
-
-static void free_dirs(struct dirent ***dirs, unsigned int num_dirs)
-{
- unsigned int i;
-
- for (i = 0; i < num_dirs; i++)
- free((*dirs)[i]);
- free(*dirs);
-}
-
-struct gpiod_chip_iter *gpiod_chip_iter_new(void)
-{
- struct gpiod_chip_iter *iter;
- struct dirent **dirs;
- int i, num_chips;
-
- num_chips = scandir("/dev", &dirs, dir_filter, alphasort);
- if (num_chips < 0)
- return NULL;
-
- iter = malloc(sizeof(*iter));
- if (!iter)
- goto err_free_dirs;
-
- iter->num_chips = num_chips;
- iter->offset = 0;
-
- if (num_chips == 0) {
- iter->chips = NULL;
- return iter;
- }
-
- iter->chips = calloc(num_chips, sizeof(*iter->chips));
- if (!iter->chips)
- goto err_free_iter;
-
- for (i = 0; i < num_chips; i++) {
- iter->chips[i] = gpiod_chip_open_by_name(dirs[i]->d_name);
- if (!iter->chips[i])
- goto err_close_chips;
- }
-
- free_dirs(&dirs, num_chips);
-
- return iter;
-
-err_close_chips:
- for (i = 0; i < num_chips; i++) {
- if (iter->chips[i])
- gpiod_chip_close(iter->chips[i]);
- }
-
- free(iter->chips);
-
-err_free_iter:
- free(iter);
-
-err_free_dirs:
- free_dirs(&dirs, num_chips);
-
- return NULL;
-}
-
-void gpiod_chip_iter_free(struct gpiod_chip_iter *iter)
-{
- if (iter->offset > 0 && iter->offset < iter->num_chips)
- gpiod_chip_close(iter->chips[iter->offset - 1]);
- gpiod_chip_iter_free_noclose(iter);
-}
-
-void gpiod_chip_iter_free_noclose(struct gpiod_chip_iter *iter)
-{
- unsigned int i;
-
- for (i = iter->offset; i < iter->num_chips; i++) {
- if (iter->chips[i])
- gpiod_chip_close(iter->chips[i]);
- }
-
- if (iter->chips)
- free(iter->chips);
-
- free(iter);
-}
-
-struct gpiod_chip *gpiod_chip_iter_next(struct gpiod_chip_iter *iter)
-{
- if (iter->offset > 0) {
- gpiod_chip_close(iter->chips[iter->offset - 1]);
- iter->chips[iter->offset - 1] = NULL;
- }
-
- return gpiod_chip_iter_next_noclose(iter);
-}
-
-struct gpiod_chip *gpiod_chip_iter_next_noclose(struct gpiod_chip_iter *iter)
-{
- return iter->offset < (iter->num_chips)
- ? iter->chips[iter->offset++] : NULL;
-}
-
-struct gpiod_line_iter *gpiod_line_iter_new(struct gpiod_chip *chip)
-{
- struct gpiod_line_iter *iter;
- unsigned int i;
-
- iter = malloc(sizeof(*iter));
- if (!iter)
- return NULL;
-
- iter->num_lines = gpiod_chip_num_lines(chip);
- iter->offset = 0;
-
- iter->lines = calloc(iter->num_lines, sizeof(*iter->lines));
- if (!iter->lines) {
- free(iter);
- return NULL;
- }
-
- for (i = 0; i < iter->num_lines; i++) {
- iter->lines[i] = gpiod_chip_get_line(chip, i);
- if (!iter->lines[i]) {
- free(iter->lines);
- free(iter);
- return NULL;
- }
- }
-
- return iter;
-}
-
-void gpiod_line_iter_free(struct gpiod_line_iter *iter)
-{
- free(iter->lines);
- free(iter);
-}
-
-struct gpiod_line *gpiod_line_iter_next(struct gpiod_line_iter *iter)
-{
- return iter->offset < (iter->num_lines)
- ? iter->lines[iter->offset++] : NULL;
-}
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-/* Misc code that didn't fit anywhere else. */
-
-#include <gpiod.h>
-
-const char *gpiod_version_string(void)
-{
- return GPIOD_VERSION_STR;
-}
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-#
-# This file is part of libgpiod.
-#
-# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
-#
-
-AM_CFLAGS = -I$(top_srcdir)/include/ -include $(top_builddir)/config.h
-AM_CFLAGS += -Wall -Wextra -g
-
-noinst_LTLIBRARIES = libtools-common.la
-libtools_common_la_SOURCES = tools-common.c tools-common.h
-
-LDADD = libtools-common.la $(top_builddir)/src/lib/libgpiod.la
-
-bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind
-
-gpiodetect_SOURCES = gpiodetect.c
-
-gpioinfo_SOURCES = gpioinfo.c
-
-gpioget_SOURCES = gpioget.c
-
-gpioset_SOURCES = gpioset.c
-
-gpiomon_SOURCES = gpiomon.c
-
-gpiofind_SOURCES = gpiofind.c
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-#include <getopt.h>
-#include <gpiod.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "tools-common.h"
-
-static const struct option longopts[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'v' },
- { GETOPT_NULL_LONGOPT },
-};
-
-static const char *const shortopts = "+hv";
-
-static void print_help(void)
-{
- printf("Usage: %s [OPTIONS]\n", get_progname());
- printf("List all GPIO chips, print their labels and number of GPIO lines.\n");
- printf("\n");
- printf("Options:\n");
- printf(" -h, --help:\t\tdisplay this message and exit\n");
- printf(" -v, --version:\tdisplay the version and exit\n");
-}
-
-int main(int argc, char **argv)
-{
- struct gpiod_chip_iter *iter;
- struct gpiod_chip *chip;
- int optc, opti;
-
- for (;;) {
- optc = getopt_long(argc, argv, shortopts, longopts, &opti);
- if (optc < 0)
- break;
-
- switch (optc) {
- case 'h':
- print_help();
- return EXIT_SUCCESS;
- case 'v':
- print_version();
- return EXIT_SUCCESS;
- case '?':
- die("try %s --help", get_progname());
- default:
- abort();
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc > 0)
- die("unrecognized argument: %s", argv[0]);
-
- iter = gpiod_chip_iter_new();
- if (!iter)
- die_perror("unable to access GPIO chips");
-
- gpiod_foreach_chip(iter, chip) {
- printf("%s [%s] (%u lines)\n",
- gpiod_chip_name(chip),
- gpiod_chip_label(chip),
- gpiod_chip_num_lines(chip));
- }
-
- gpiod_chip_iter_free(iter);
-
- return EXIT_SUCCESS;
-}
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-#include <getopt.h>
-#include <gpiod.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "tools-common.h"
-
-static const struct option longopts[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'v' },
- { GETOPT_NULL_LONGOPT },
-};
-
-static const char *const shortopts = "+hv";
-
-static void print_help(void)
-{
- printf("Usage: %s [OPTIONS] <name>\n", get_progname());
- printf("Find a GPIO line by name. The output of this command can be used as input for gpioget/set.\n");
- printf("\n");
- printf("Options:\n");
- printf(" -h, --help:\t\tdisplay this message and exit\n");
- printf(" -v, --version:\tdisplay the version and exit\n");
-}
-
-int main(int argc, char **argv)
-{
- unsigned int offset;
- int optc, opti, rv;
- char chip[32];
-
- for (;;) {
- optc = getopt_long(argc, argv, shortopts, longopts, &opti);
- if (optc < 0)
- break;
-
- switch (optc) {
- case 'h':
- print_help();
- return EXIT_SUCCESS;
- case 'v':
- print_version();
- return EXIT_SUCCESS;
- case '?':
- die("try %s --help", get_progname());
- default:
- abort();
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc != 1)
- die("exactly one GPIO line name must be specified");
-
- rv = gpiod_ctxless_find_line(argv[0], chip, sizeof(chip), &offset);
- if (rv < 0)
- die_perror("error performing the line lookup");
- else if (rv == 0)
- return EXIT_FAILURE;
-
- printf("%s %u\n", chip, offset);
-
- return EXIT_SUCCESS;
-}
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-#include <getopt.h>
-#include <gpiod.h>
-#include <limits.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "tools-common.h"
-
-static const struct option longopts[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'v' },
- { "active-low", no_argument, NULL, 'l' },
- { GETOPT_NULL_LONGOPT },
-};
-
-static const char *const shortopts = "+hvl";
-
-static void print_help(void)
-{
- printf("Usage: %s [OPTIONS] <chip name/number> <offset 1> <offset 2> ...\n",
- get_progname());
- printf("Read line value(s) from a GPIO chip\n");
- printf("\n");
- printf("Options:\n");
- printf(" -h, --help:\t\tdisplay this message and exit\n");
- printf(" -v, --version:\tdisplay the version and exit\n");
- printf(" -l, --active-low:\tset the line active state to low\n");
-}
-
-int main(int argc, char **argv)
-{
- unsigned int *offsets, i, num_lines;
- int *values, optc, opti, rv;
- bool active_low = false;
- char *device, *end;
-
- for (;;) {
- optc = getopt_long(argc, argv, shortopts, longopts, &opti);
- if (optc < 0)
- break;
-
- switch (optc) {
- case 'h':
- print_help();
- return EXIT_SUCCESS;
- case 'v':
- print_version();
- return EXIT_SUCCESS;
- case 'l':
- active_low = true;
- break;
- case '?':
- die("try %s --help", get_progname());
- default:
- abort();
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc < 1)
- die("gpiochip must be specified");
-
- if (argc < 2)
- die("at least one GPIO line offset must be specified");
-
- device = argv[0];
- num_lines = argc - 1;
-
- values = malloc(sizeof(*values) * num_lines);
- offsets = malloc(sizeof(*offsets) * num_lines);
- if (!values || !offsets)
- die("out of memory");
-
- for (i = 0; i < num_lines; i++) {
- offsets[i] = strtoul(argv[i + 1], &end, 10);
- if (*end != '\0' || offsets[i] > INT_MAX)
- die("invalid GPIO offset: %s", argv[i + 1]);
- }
-
- rv = gpiod_ctxless_get_value_multiple(device, offsets, values,
- num_lines, active_low,
- "gpioget");
- if (rv < 0)
- die_perror("error reading GPIO values");
-
- for (i = 0; i < num_lines; i++) {
- printf("%d", values[i]);
- if (i != num_lines - 1)
- printf(" ");
- }
- printf("\n");
-
- free(values);
- free(offsets);
-
- return EXIT_SUCCESS;
-}
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-#include <errno.h>
-#include <getopt.h>
-#include <gpiod.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "tools-common.h"
-
-typedef bool (*is_set_func)(struct gpiod_line *);
-
-struct flag {
- const char *name;
- is_set_func is_set;
-};
-
-static const struct flag flags[] = {
- {
- .name = "used",
- .is_set = gpiod_line_is_used,
- },
- {
- .name = "open-drain",
- .is_set = gpiod_line_is_open_drain,
- },
- {
- .name = "open-source",
- .is_set = gpiod_line_is_open_source,
- },
-};
-
-static const struct option longopts[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'v' },
- { GETOPT_NULL_LONGOPT },
-};
-
-static const char *const shortopts = "+hv";
-
-static void print_help(void)
-{
- printf("Usage: %s [OPTIONS] <gpiochip1> ...\n", get_progname());
- printf("Print information about all lines of the specified GPIO chip(s) (or all gpiochips if none are specified).\n");
- printf("\n");
- printf("Options:\n");
- printf(" -h, --help:\t\tdisplay this message and exit\n");
- printf(" -v, --version:\tdisplay the version and exit\n");
-}
-
-static PRINTF(3, 4) void prinfo(bool *of,
- unsigned int prlen, const char *fmt, ...)
-{
- char *buf, *buffmt = NULL;
- size_t len;
- va_list va;
- int rv;
-
- va_start(va, fmt);
- rv = vasprintf(&buf, fmt, va);
- va_end(va);
- if (rv < 0)
- die("vasprintf: %s\n", strerror(errno));
-
- len = strlen(buf) - 1;
-
- if (len >= prlen || *of) {
- *of = true;
- printf("%s", buf);
- } else {
- rv = asprintf(&buffmt, "%%%us", prlen);
- if (rv < 0)
- die("asprintf: %s\n", strerror(errno));
-
- printf(buffmt, buf);
- }
-
- free(buf);
- if (fmt)
- free(buffmt);
-}
-
-static void list_lines(struct gpiod_chip *chip)
-{
- struct gpiod_line_iter *iter;
- int direction, active_state;
- const char *name, *consumer;
- struct gpiod_line *line;
- unsigned int i, offset;
- bool flag_printed, of;
-
- iter = gpiod_line_iter_new(chip);
- if (!iter)
- die_perror("error creating line iterator");
-
- printf("%s - %u lines:\n",
- gpiod_chip_name(chip), gpiod_chip_num_lines(chip));
-
- gpiod_foreach_line(iter, line) {
- offset = gpiod_line_offset(line);
- name = gpiod_line_name(line);
- consumer = gpiod_line_consumer(line);
- direction = gpiod_line_direction(line);
- active_state = gpiod_line_active_state(line);
-
- of = false;
-
- printf("\tline ");
- prinfo(&of, 3, "%u", offset);
- printf(": ");
-
- name ? prinfo(&of, 12, "\"%s\"", name)
- : prinfo(&of, 12, "unnamed");
- printf(" ");
-
- consumer ? prinfo(&of, 12, "\"%s\"", consumer)
- : prinfo(&of, 12, "unused");
- printf(" ");
-
- prinfo(&of, 8, "%s ", direction == GPIOD_LINE_DIRECTION_INPUT
- ? "input" : "output");
- prinfo(&of, 13, "%s ",
- active_state == GPIOD_LINE_ACTIVE_STATE_LOW
- ? "active-low"
- : "active-high");
-
- flag_printed = false;
- for (i = 0; i < ARRAY_SIZE(flags); i++) {
- if (flags[i].is_set(line)) {
- if (flag_printed)
- printf(" ");
- else
- printf("[");
- printf("%s", flags[i].name);
- flag_printed = true;
- }
- }
- if (flag_printed)
- printf("]");
-
- printf("\n");
- }
-
- gpiod_line_iter_free(iter);
-}
-
-int main(int argc, char **argv)
-{
- struct gpiod_chip_iter *chip_iter;
- struct gpiod_chip *chip;
- int i, optc, opti;
-
- for (;;) {
- optc = getopt_long(argc, argv, shortopts, longopts, &opti);
- if (optc < 0)
- break;
-
- switch (optc) {
- case 'h':
- print_help();
- return EXIT_SUCCESS;
- case 'v':
- print_version();
- return EXIT_SUCCESS;
- case '?':
- die("try %s --help", get_progname());
- default:
- abort();
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc == 0) {
- chip_iter = gpiod_chip_iter_new();
- if (!chip_iter)
- die_perror("error accessing GPIO chips");
-
- gpiod_foreach_chip(chip_iter, chip)
- list_lines(chip);
-
- gpiod_chip_iter_free(chip_iter);
- } else {
- for (i = 0; i < argc; i++) {
- chip = gpiod_chip_open_lookup(argv[i]);
- if (!chip)
- die_perror("looking up chip %s", argv[i]);
-
- list_lines(chip);
-
- gpiod_chip_close(chip);
- }
- }
-
- return EXIT_SUCCESS;
-}
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-#include <errno.h>
-#include <getopt.h>
-#include <gpiod.h>
-#include <limits.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/signalfd.h>
-#include <unistd.h>
-
-#include "tools-common.h"
-
-static const struct option longopts[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'v' },
- { "active-low", no_argument, NULL, 'l' },
- { "num-events", required_argument, NULL, 'n' },
- { "silent", no_argument, NULL, 's' },
- { "rising-edge", no_argument, NULL, 'r' },
- { "falling-edge", no_argument, NULL, 'f' },
- { "line-buffered", no_argument, NULL, 'b' },
- { "format", required_argument, NULL, 'F' },
- { GETOPT_NULL_LONGOPT },
-};
-
-static const char *const shortopts = "+hvln:srfbF:";
-
-static void print_help(void)
-{
- printf("Usage: %s [OPTIONS] <chip name/number> <offset 1> <offset 2> ...\n",
- get_progname());
- printf("Wait for events on GPIO lines and print them to standard output\n");
- printf("\n");
- printf("Options:\n");
- printf(" -h, --help:\t\tdisplay this message and exit\n");
- printf(" -v, --version:\tdisplay the version and exit\n");
- printf(" -l, --active-low:\tset the line active state to low\n");
- printf(" -n, --num-events=NUM:\texit after processing NUM events\n");
- printf(" -s, --silent:\t\tdon't print event info\n");
- printf(" -r, --rising-edge:\tonly process rising edge events\n");
- printf(" -f, --falling-edge:\tonly process falling edge events\n");
- printf(" -b, --line-buffered:\tset standard output as line buffered\n");
- printf(" -F, --format=FMT\tspecify custom output format\n");
- printf("\n");
- printf("Format specifiers:\n");
- printf(" %%o: GPIO line offset\n");
- printf(" %%e: event type (0 - falling edge, 1 rising edge)\n");
- printf(" %%s: seconds part of the event timestamp\n");
- printf(" %%n: nanoseconds part of the event timestamp\n");
-}
-
-struct mon_ctx {
- unsigned int offset;
- unsigned int events_wanted;
- unsigned int events_done;
-
- bool silent;
- char *fmt;
-
- int sigfd;
-};
-
-static void event_print_custom(unsigned int offset,
- const struct timespec *ts,
- int event_type,
- struct mon_ctx *ctx)
-{
- char *prev, *curr, fmt;
-
- for (prev = curr = ctx->fmt;;) {
- curr = strchr(curr, '%');
- if (!curr) {
- fputs(prev, stdout);
- break;
- }
-
- if (prev != curr)
- fwrite(prev, curr - prev, 1, stdout);
-
- fmt = *(curr + 1);
-
- switch (fmt) {
- case 'o':
- printf("%u", offset);
- break;
- case 'e':
- if (event_type == GPIOD_CTXLESS_EVENT_CB_RISING_EDGE)
- fputc('1', stdout);
- else
- fputc('0', stdout);
- break;
- case 's':
- printf("%ld", ts->tv_sec);
- break;
- case 'n':
- printf("%ld", ts->tv_nsec);
- break;
- case '%':
- fputc('%', stdout);
- break;
- case '\0':
- fputc('%', stdout);
- goto end;
- default:
- fwrite(curr, 2, 1, stdout);
- break;
- }
-
- curr += 2;
- prev = curr;
- }
-
-end:
- fputc('\n', stdout);
-}
-
-static void event_print_human_readable(unsigned int offset,
- const struct timespec *ts,
- int event_type)
-{
- char *evname;
-
- if (event_type == GPIOD_CTXLESS_EVENT_CB_RISING_EDGE)
- evname = " RISING EDGE";
- else
- evname = "FALLING EDGE";
-
- printf("event: %s offset: %u timestamp: [%8ld.%09ld]\n",
- evname, offset, ts->tv_sec, ts->tv_nsec);
-}
-
-static int poll_callback(unsigned int num_lines,
- struct gpiod_ctxless_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, rv;
- unsigned int i;
-
- for (i = 0; i < num_lines; i++) {
- pfds[i].fd = fds[i].fd;
- pfds[i].events = POLLIN | POLLPRI;
- }
-
- pfds[i].fd = ctx->sigfd;
- pfds[i].events = POLLIN | POLLPRI;
-
- ts = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;
-
- cnt = poll(pfds, num_lines + 1, ts);
- if (cnt < 0)
- return GPIOD_CTXLESS_EVENT_POLL_RET_ERR;
- else if (cnt == 0)
- return GPIOD_CTXLESS_EVENT_POLL_RET_TIMEOUT;
-
- rv = cnt;
- for (i = 0; i < num_lines; i++) {
- if (pfds[i].revents) {
- fds[i].event = true;
- if (!--cnt)
- return rv;
- }
- }
-
- /*
- * If we're here, then there's a signal pending. No need to read it,
- * we know we should quit now.
- */
- close(ctx->sigfd);
-
- return GPIOD_CTXLESS_EVENT_POLL_RET_STOP;
-}
-
-static void handle_event(struct mon_ctx *ctx, int event_type,
- unsigned int line_offset,
- const struct timespec *timestamp)
-{
- if (!ctx->silent) {
- if (ctx->fmt)
- event_print_custom(line_offset, timestamp,
- event_type, ctx);
- else
- event_print_human_readable(line_offset,
- timestamp, event_type);
- }
-
- ctx->events_done++;
-}
-
-static int event_callback(int event_type, unsigned int line_offset,
- const struct timespec *timestamp, void *data)
-{
- struct mon_ctx *ctx = data;
-
- switch (event_type) {
- case GPIOD_CTXLESS_EVENT_CB_RISING_EDGE:
- case GPIOD_CTXLESS_EVENT_CB_FALLING_EDGE:
- 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;
- }
-
- if (ctx->events_wanted && ctx->events_done >= ctx->events_wanted)
- return GPIOD_CTXLESS_EVENT_CB_RET_STOP;
-
- return GPIOD_CTXLESS_EVENT_CB_RET_OK;
-}
-
-static int make_signalfd(void)
-{
- sigset_t sigmask;
- int sigfd, rv;
-
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGTERM);
- sigaddset(&sigmask, SIGINT);
-
- rv = sigprocmask(SIG_BLOCK, &sigmask, NULL);
- if (rv < 0)
- die("error masking signals: %s", strerror(errno));
-
- sigfd = signalfd(-1, &sigmask, 0);
- if (sigfd < 0)
- die("error creating signalfd: %s", strerror(errno));
-
- return sigfd;
-}
-
-int main(int argc, char **argv)
-{
- 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 };
- int optc, opti, rv, i, event_type;
- struct mon_ctx ctx;
- char *end;
-
- memset(&ctx, 0, sizeof(ctx));
-
- for (;;) {
- optc = getopt_long(argc, argv, shortopts, longopts, &opti);
- if (optc < 0)
- break;
-
- switch (optc) {
- case 'h':
- print_help();
- return EXIT_SUCCESS;
- case 'v':
- print_version();
- return EXIT_SUCCESS;
- case 'l':
- active_low = true;
- break;
- case 'n':
- ctx.events_wanted = strtoul(optarg, &end, 10);
- if (*end != '\0')
- die("invalid number: %s", optarg);
- break;
- case 's':
- ctx.silent = true;
- break;
- case 'r':
- watch_rising = true;
- break;
- case 'f':
- watch_falling = true;
- break;
- case 'b':
- setlinebuf(stdout);
- break;
- case 'F':
- ctx.fmt = optarg;
- break;
- case '?':
- die("try %s --help", get_progname());
- default:
- abort();
- }
- }
-
- argc -= optind;
- argv += optind;
-
- 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");
-
- if (argc < 2)
- die("at least one GPIO line offset must be specified");
-
- for (i = 1; i < argc; i++) {
- offset = strtoul(argv[i], &end, 10);
- if (*end != '\0' || offset > INT_MAX)
- die("invalid GPIO offset: %s", argv[i]);
-
- offsets[i - 1] = offset;
- num_lines++;
- }
-
- ctx.sigfd = make_signalfd();
-
- rv = gpiod_ctxless_event_monitor_multiple(argv[0], event_type,
- offsets, num_lines,
- active_low, "gpiomon",
- &timeout, poll_callback,
- event_callback, &ctx);
- if (rv)
- die_perror("error waiting for events");
-
- return EXIT_SUCCESS;
-}
+++ /dev/null
-// SPDX-License-Identifier: LGPL-2.1-or-later
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-#include <errno.h>
-#include <gpiod.h>
-#include <getopt.h>
-#include <limits.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/select.h>
-#include <sys/signalfd.h>
-#include <unistd.h>
-
-#include "tools-common.h"
-
-static const struct option longopts[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'v' },
- { "active-low", no_argument, NULL, 'l' },
- { "mode", required_argument, NULL, 'm' },
- { "sec", required_argument, NULL, 's' },
- { "usec", required_argument, NULL, 'u' },
- { "background", no_argument, NULL, 'b' },
- { GETOPT_NULL_LONGOPT },
-};
-
-static const char *const shortopts = "+hvlm:s:u:b";
-
-static void print_help(void)
-{
- printf("Usage: %s [OPTIONS] <chip name/number> <offset1>=<value1> <offset2>=<value2> ...\n",
- get_progname());
- printf("Set GPIO line values of a GPIO chip and maintain the state until the process exits\n");
- printf("\n");
- printf("Options:\n");
- printf(" -h, --help:\t\tdisplay this message and exit\n");
- printf(" -v, --version:\tdisplay the version and exit\n");
- printf(" -l, --active-low:\tset the line active state to low\n");
- printf(" -m, --mode=[exit|wait|time|signal] (defaults to 'exit'):\n");
- printf(" tell the program what to do after setting values\n");
- printf(" -s, --sec=SEC:\tspecify the number of seconds to wait (only valid for --mode=time)\n");
- printf(" -u, --usec=USEC:\tspecify the number of microseconds to wait (only valid for --mode=time)\n");
- printf(" -b, --background:\tafter setting values: detach from the controlling terminal\n");
- printf("\n");
- printf("Modes:\n");
- printf(" exit:\t\tset values and exit immediately\n");
- printf(" wait:\t\tset values and wait for user to press ENTER\n");
- printf(" time:\t\tset values and sleep for a specified amount of time\n");
- printf(" signal:\tset values and wait for SIGINT or SIGTERM\n");
- printf("\n");
- printf("Note: the state of a GPIO line controlled over the character device reverts to default\n");
- printf("when the last process referencing the file descriptor representing the device file exits.\n");
- printf("This means that it's wrong to run gpioset, have it exit and expect the line to continue\n");
- printf("being driven high or low. It may happen if given pin is floating but it must be interpreted\n");
- printf("as undefined behavior.\n");
-}
-
-struct callback_data {
- /* Replace with a union once we have more modes using callback data. */
- struct timeval tv;
- bool daemonize;
-};
-
-static void maybe_daemonize(bool daemonize)
-{
- int rv;
-
- if (daemonize) {
- rv = daemon(0, 0);
- if (rv < 0)
- die("unable to daemonize: %s", strerror(errno));
- }
-}
-
-static void wait_enter(void *data GPIOD_UNUSED)
-{
- getchar();
-}
-
-static void wait_time(void *data)
-{
- struct callback_data *cbdata = data;
-
- maybe_daemonize(cbdata->daemonize);
- select(0, NULL, NULL, NULL, &cbdata->tv);
-}
-
-static void wait_signal(void *data)
-{
- struct callback_data *cbdata = data;
- struct pollfd pfd;
- sigset_t sigmask;
- int sigfd, rv;
-
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGTERM);
- sigaddset(&sigmask, SIGINT);
-
- rv = sigprocmask(SIG_BLOCK, &sigmask, NULL);
- if (rv < 0)
- die("error blocking signals: %s", strerror(errno));
-
- sigfd = signalfd(-1, &sigmask, 0);
- if (sigfd < 0)
- die("error creating signalfd: %s", strerror(errno));
-
- memset(&pfd, 0, sizeof(pfd));
- pfd.fd = sigfd;
- pfd.events = POLLIN | POLLPRI;
-
- maybe_daemonize(cbdata->daemonize);
-
- for (;;) {
- rv = poll(&pfd, 1, 1000 /* one second */);
- if (rv < 0)
- die("error polling for signals: %s", strerror(errno));
- else if (rv > 0)
- break;
- }
-
- /*
- * Don't bother reading siginfo - it's enough to know that we
- * received any signal.
- */
- close(sigfd);
-}
-
-enum {
- MODE_EXIT = 0,
- MODE_WAIT,
- MODE_TIME,
- MODE_SIGNAL,
-};
-
-struct mode_mapping {
- int id;
- const char *name;
- gpiod_ctxless_set_value_cb callback;
-};
-
-static const struct mode_mapping modes[] = {
- [MODE_EXIT] = {
- .id = MODE_EXIT,
- .name = "exit",
- .callback = NULL,
- },
- [MODE_WAIT] = {
- .id = MODE_WAIT,
- .name = "wait",
- .callback = wait_enter,
- },
- [MODE_TIME] = {
- .id = MODE_TIME,
- .name = "time",
- .callback = wait_time,
- },
- [MODE_SIGNAL] = {
- .id = MODE_SIGNAL,
- .name = "signal",
- .callback = wait_signal,
- },
-};
-
-static const struct mode_mapping *parse_mode(const char *mode)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(modes); i++)
- if (strcmp(mode, modes[i].name) == 0)
- return &modes[i];
-
- return NULL;
-}
-
-int main(int argc, char **argv)
-{
- const struct mode_mapping *mode = &modes[MODE_EXIT];
- unsigned int *offsets, num_lines, i;
- int *values, rv, optc, opti;
- struct callback_data cbdata;
- bool active_low = false;
- char *device, *end;
-
- memset(&cbdata, 0, sizeof(cbdata));
-
- for (;;) {
- optc = getopt_long(argc, argv, shortopts, longopts, &opti);
- if (optc < 0)
- break;
-
- switch (optc) {
- case 'h':
- print_help();
- return EXIT_SUCCESS;
- case 'v':
- print_version();
- return EXIT_SUCCESS;
- case 'l':
- active_low = true;
- break;
- case 'm':
- mode = parse_mode(optarg);
- if (!mode)
- die("invalid mode: %s", optarg);
- break;
- case 's':
- cbdata.tv.tv_sec = strtoul(optarg, &end, 10);
- if (*end != '\0')
- die("invalid time value in seconds: %s", optarg);
- break;
- case 'u':
- cbdata.tv.tv_usec = strtoul(optarg, &end, 10);
- if (*end != '\0')
- die("invalid time value in microseconds: %s",
- optarg);
- break;
- case 'b':
- cbdata.daemonize = true;
- break;
- case '?':
- die("try %s --help", get_progname());
- default:
- abort();
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (mode->id != MODE_TIME && (cbdata.tv.tv_sec || cbdata.tv.tv_usec))
- die("can't specify wait time in this mode");
-
- if (mode->id != MODE_SIGNAL &&
- mode->id != MODE_TIME &&
- cbdata.daemonize)
- die("can't daemonize in this mode");
-
- if (argc < 1)
- die("gpiochip must be specified");
-
- if (argc < 2)
- die("at least one GPIO line offset to value mapping must be specified");
-
- device = argv[0];
-
- num_lines = argc - 1;
-
- offsets = malloc(sizeof(*offsets) * num_lines);
- values = malloc(sizeof(*values) * num_lines);
- if (!values || !offsets)
- die("out of memory");
-
- for (i = 0; i < num_lines; i++) {
- rv = sscanf(argv[i + 1], "%u=%d", &offsets[i], &values[i]);
- if (rv != 2)
- die("invalid offset<->value mapping: %s", argv[i + 1]);
-
- if (values[i] != 0 && values[i] != 1)
- die("value must be 0 or 1: %s", argv[i + 1]);
-
- if (offsets[i] > INT_MAX)
- die("invalid offset: %s", argv[i + 1]);
- }
-
- rv = gpiod_ctxless_set_value_multiple(device, offsets, values,
- num_lines, active_low, "gpioset",
- mode->callback, &cbdata);
- if (rv < 0)
- die_perror("error setting the GPIO line values");
-
- free(offsets);
- free(values);
-
- return EXIT_SUCCESS;
-}
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-/* Common code for GPIO tools. */
-
-#include <errno.h>
-#include <gpiod.h>
-#include <libgen.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "tools-common.h"
-
-const char *get_progname(void)
-{
- return program_invocation_name;
-}
-
-void die(const char *fmt, ...)
-{
- va_list va;
-
- va_start(va, fmt);
- fprintf(stderr, "%s: ", program_invocation_name);
- vfprintf(stderr, fmt, va);
- fprintf(stderr, "\n");
- va_end(va);
-
- exit(EXIT_FAILURE);
-}
-
-void die_perror(const char *fmt, ...)
-{
- va_list va;
-
- va_start(va, fmt);
- fprintf(stderr, "%s: ", program_invocation_name);
- vfprintf(stderr, fmt, va);
- fprintf(stderr, ": %s\n", strerror(errno));
- va_end(va);
-
- exit(EXIT_FAILURE);
-}
-
-void print_version(void)
-{
- printf("%s (libgpiod) v%s\n",
- program_invocation_short_name, gpiod_version_string());
- printf("Copyright (C) 2017-2018 Bartosz Golaszewski\n");
- printf("License: LGPLv2.1\n");
- printf("This is free software: you are free to change and redistribute it.\n");
- printf("There is NO WARRANTY, to the extent permitted by law.\n");
-}
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * This file is part of libgpiod.
- *
- * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
- */
-
-#ifndef __GPIOD_TOOLS_COMMON_H__
-#define __GPIOD_TOOLS_COMMON_H__
-
-/*
- * Various helpers for the GPIO tools.
- *
- * NOTE: This is not a stable interface - it's only to avoid duplicating
- * common code.
- */
-
-#define NORETURN __attribute__((noreturn))
-#define PRINTF(fmt, arg) __attribute__((format(printf, fmt, arg)))
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
-
-#define GETOPT_NULL_LONGOPT NULL, 0, NULL, 0
-
-const char *get_progname(void);
-void die(const char *fmt, ...) NORETURN PRINTF(1, 2);
-void die_perror(const char *fmt, ...) NORETURN PRINTF(1, 2);
-void print_version(void);
-
-#endif /* __GPIOD_TOOLS_COMMON_H__ */
AM_CFLAGS = -I$(top_srcdir)/include/ -include $(top_builddir)/config.h
AM_CFLAGS += -Wall -Wextra -g $(KMOD_CFLAGS) $(UDEV_CFLAGS)
AM_LDFLAGS = -pthread
-LDADD = $(top_builddir)/src/lib/libgpiod.la $(KMOD_LIBS) $(UDEV_LIBS)
+LDADD = $(top_builddir)/lib/libgpiod.la $(KMOD_LIBS) $(UDEV_LIBS)
check_PROGRAMS = gpiod-test
progpath = xstrdup(program_invocation_name);
progdir = dirname(progpath);
- path = xappend(NULL, "%s/../../src/tools/%s", progdir, tool);
+ path = xappend(NULL, "%s/../../tools/%s", progdir, tool);
free(progpath);
return path;
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+#
+# This file is part of libgpiod.
+#
+# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+
+AM_CFLAGS = -I$(top_srcdir)/include/ -include $(top_builddir)/config.h
+AM_CFLAGS += -Wall -Wextra -g
+
+noinst_LTLIBRARIES = libtools-common.la
+libtools_common_la_SOURCES = tools-common.c tools-common.h
+
+LDADD = libtools-common.la $(top_builddir)/lib/libgpiod.la
+
+bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind
+
+gpiodetect_SOURCES = gpiodetect.c
+
+gpioinfo_SOURCES = gpioinfo.c
+
+gpioget_SOURCES = gpioget.c
+
+gpioset_SOURCES = gpioset.c
+
+gpiomon_SOURCES = gpiomon.c
+
+gpiofind_SOURCES = gpiofind.c
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+#include <getopt.h>
+#include <gpiod.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "tools-common.h"
+
+static const struct option longopts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { GETOPT_NULL_LONGOPT },
+};
+
+static const char *const shortopts = "+hv";
+
+static void print_help(void)
+{
+ printf("Usage: %s [OPTIONS]\n", get_progname());
+ printf("List all GPIO chips, print their labels and number of GPIO lines.\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -h, --help:\t\tdisplay this message and exit\n");
+ printf(" -v, --version:\tdisplay the version and exit\n");
+}
+
+int main(int argc, char **argv)
+{
+ struct gpiod_chip_iter *iter;
+ struct gpiod_chip *chip;
+ int optc, opti;
+
+ for (;;) {
+ optc = getopt_long(argc, argv, shortopts, longopts, &opti);
+ if (optc < 0)
+ break;
+
+ switch (optc) {
+ case 'h':
+ print_help();
+ return EXIT_SUCCESS;
+ case 'v':
+ print_version();
+ return EXIT_SUCCESS;
+ case '?':
+ die("try %s --help", get_progname());
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ die("unrecognized argument: %s", argv[0]);
+
+ iter = gpiod_chip_iter_new();
+ if (!iter)
+ die_perror("unable to access GPIO chips");
+
+ gpiod_foreach_chip(iter, chip) {
+ printf("%s [%s] (%u lines)\n",
+ gpiod_chip_name(chip),
+ gpiod_chip_label(chip),
+ gpiod_chip_num_lines(chip));
+ }
+
+ gpiod_chip_iter_free(iter);
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+#include <getopt.h>
+#include <gpiod.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "tools-common.h"
+
+static const struct option longopts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { GETOPT_NULL_LONGOPT },
+};
+
+static const char *const shortopts = "+hv";
+
+static void print_help(void)
+{
+ printf("Usage: %s [OPTIONS] <name>\n", get_progname());
+ printf("Find a GPIO line by name. The output of this command can be used as input for gpioget/set.\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -h, --help:\t\tdisplay this message and exit\n");
+ printf(" -v, --version:\tdisplay the version and exit\n");
+}
+
+int main(int argc, char **argv)
+{
+ unsigned int offset;
+ int optc, opti, rv;
+ char chip[32];
+
+ for (;;) {
+ optc = getopt_long(argc, argv, shortopts, longopts, &opti);
+ if (optc < 0)
+ break;
+
+ switch (optc) {
+ case 'h':
+ print_help();
+ return EXIT_SUCCESS;
+ case 'v':
+ print_version();
+ return EXIT_SUCCESS;
+ case '?':
+ die("try %s --help", get_progname());
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ die("exactly one GPIO line name must be specified");
+
+ rv = gpiod_ctxless_find_line(argv[0], chip, sizeof(chip), &offset);
+ if (rv < 0)
+ die_perror("error performing the line lookup");
+ else if (rv == 0)
+ return EXIT_FAILURE;
+
+ printf("%s %u\n", chip, offset);
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+#include <getopt.h>
+#include <gpiod.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "tools-common.h"
+
+static const struct option longopts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { "active-low", no_argument, NULL, 'l' },
+ { GETOPT_NULL_LONGOPT },
+};
+
+static const char *const shortopts = "+hvl";
+
+static void print_help(void)
+{
+ printf("Usage: %s [OPTIONS] <chip name/number> <offset 1> <offset 2> ...\n",
+ get_progname());
+ printf("Read line value(s) from a GPIO chip\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -h, --help:\t\tdisplay this message and exit\n");
+ printf(" -v, --version:\tdisplay the version and exit\n");
+ printf(" -l, --active-low:\tset the line active state to low\n");
+}
+
+int main(int argc, char **argv)
+{
+ unsigned int *offsets, i, num_lines;
+ int *values, optc, opti, rv;
+ bool active_low = false;
+ char *device, *end;
+
+ for (;;) {
+ optc = getopt_long(argc, argv, shortopts, longopts, &opti);
+ if (optc < 0)
+ break;
+
+ switch (optc) {
+ case 'h':
+ print_help();
+ return EXIT_SUCCESS;
+ case 'v':
+ print_version();
+ return EXIT_SUCCESS;
+ case 'l':
+ active_low = true;
+ break;
+ case '?':
+ die("try %s --help", get_progname());
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ die("gpiochip must be specified");
+
+ if (argc < 2)
+ die("at least one GPIO line offset must be specified");
+
+ device = argv[0];
+ num_lines = argc - 1;
+
+ values = malloc(sizeof(*values) * num_lines);
+ offsets = malloc(sizeof(*offsets) * num_lines);
+ if (!values || !offsets)
+ die("out of memory");
+
+ for (i = 0; i < num_lines; i++) {
+ offsets[i] = strtoul(argv[i + 1], &end, 10);
+ if (*end != '\0' || offsets[i] > INT_MAX)
+ die("invalid GPIO offset: %s", argv[i + 1]);
+ }
+
+ rv = gpiod_ctxless_get_value_multiple(device, offsets, values,
+ num_lines, active_low,
+ "gpioget");
+ if (rv < 0)
+ die_perror("error reading GPIO values");
+
+ for (i = 0; i < num_lines; i++) {
+ printf("%d", values[i]);
+ if (i != num_lines - 1)
+ printf(" ");
+ }
+ printf("\n");
+
+ free(values);
+ free(offsets);
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <gpiod.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "tools-common.h"
+
+typedef bool (*is_set_func)(struct gpiod_line *);
+
+struct flag {
+ const char *name;
+ is_set_func is_set;
+};
+
+static const struct flag flags[] = {
+ {
+ .name = "used",
+ .is_set = gpiod_line_is_used,
+ },
+ {
+ .name = "open-drain",
+ .is_set = gpiod_line_is_open_drain,
+ },
+ {
+ .name = "open-source",
+ .is_set = gpiod_line_is_open_source,
+ },
+};
+
+static const struct option longopts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { GETOPT_NULL_LONGOPT },
+};
+
+static const char *const shortopts = "+hv";
+
+static void print_help(void)
+{
+ printf("Usage: %s [OPTIONS] <gpiochip1> ...\n", get_progname());
+ printf("Print information about all lines of the specified GPIO chip(s) (or all gpiochips if none are specified).\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -h, --help:\t\tdisplay this message and exit\n");
+ printf(" -v, --version:\tdisplay the version and exit\n");
+}
+
+static PRINTF(3, 4) void prinfo(bool *of,
+ unsigned int prlen, const char *fmt, ...)
+{
+ char *buf, *buffmt = NULL;
+ size_t len;
+ va_list va;
+ int rv;
+
+ va_start(va, fmt);
+ rv = vasprintf(&buf, fmt, va);
+ va_end(va);
+ if (rv < 0)
+ die("vasprintf: %s\n", strerror(errno));
+
+ len = strlen(buf) - 1;
+
+ if (len >= prlen || *of) {
+ *of = true;
+ printf("%s", buf);
+ } else {
+ rv = asprintf(&buffmt, "%%%us", prlen);
+ if (rv < 0)
+ die("asprintf: %s\n", strerror(errno));
+
+ printf(buffmt, buf);
+ }
+
+ free(buf);
+ if (fmt)
+ free(buffmt);
+}
+
+static void list_lines(struct gpiod_chip *chip)
+{
+ struct gpiod_line_iter *iter;
+ int direction, active_state;
+ const char *name, *consumer;
+ struct gpiod_line *line;
+ unsigned int i, offset;
+ bool flag_printed, of;
+
+ iter = gpiod_line_iter_new(chip);
+ if (!iter)
+ die_perror("error creating line iterator");
+
+ printf("%s - %u lines:\n",
+ gpiod_chip_name(chip), gpiod_chip_num_lines(chip));
+
+ gpiod_foreach_line(iter, line) {
+ offset = gpiod_line_offset(line);
+ name = gpiod_line_name(line);
+ consumer = gpiod_line_consumer(line);
+ direction = gpiod_line_direction(line);
+ active_state = gpiod_line_active_state(line);
+
+ of = false;
+
+ printf("\tline ");
+ prinfo(&of, 3, "%u", offset);
+ printf(": ");
+
+ name ? prinfo(&of, 12, "\"%s\"", name)
+ : prinfo(&of, 12, "unnamed");
+ printf(" ");
+
+ consumer ? prinfo(&of, 12, "\"%s\"", consumer)
+ : prinfo(&of, 12, "unused");
+ printf(" ");
+
+ prinfo(&of, 8, "%s ", direction == GPIOD_LINE_DIRECTION_INPUT
+ ? "input" : "output");
+ prinfo(&of, 13, "%s ",
+ active_state == GPIOD_LINE_ACTIVE_STATE_LOW
+ ? "active-low"
+ : "active-high");
+
+ flag_printed = false;
+ for (i = 0; i < ARRAY_SIZE(flags); i++) {
+ if (flags[i].is_set(line)) {
+ if (flag_printed)
+ printf(" ");
+ else
+ printf("[");
+ printf("%s", flags[i].name);
+ flag_printed = true;
+ }
+ }
+ if (flag_printed)
+ printf("]");
+
+ printf("\n");
+ }
+
+ gpiod_line_iter_free(iter);
+}
+
+int main(int argc, char **argv)
+{
+ struct gpiod_chip_iter *chip_iter;
+ struct gpiod_chip *chip;
+ int i, optc, opti;
+
+ for (;;) {
+ optc = getopt_long(argc, argv, shortopts, longopts, &opti);
+ if (optc < 0)
+ break;
+
+ switch (optc) {
+ case 'h':
+ print_help();
+ return EXIT_SUCCESS;
+ case 'v':
+ print_version();
+ return EXIT_SUCCESS;
+ case '?':
+ die("try %s --help", get_progname());
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ chip_iter = gpiod_chip_iter_new();
+ if (!chip_iter)
+ die_perror("error accessing GPIO chips");
+
+ gpiod_foreach_chip(chip_iter, chip)
+ list_lines(chip);
+
+ gpiod_chip_iter_free(chip_iter);
+ } else {
+ for (i = 0; i < argc; i++) {
+ chip = gpiod_chip_open_lookup(argv[i]);
+ if (!chip)
+ die_perror("looking up chip %s", argv[i]);
+
+ list_lines(chip);
+
+ gpiod_chip_close(chip);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <gpiod.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/signalfd.h>
+#include <unistd.h>
+
+#include "tools-common.h"
+
+static const struct option longopts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { "active-low", no_argument, NULL, 'l' },
+ { "num-events", required_argument, NULL, 'n' },
+ { "silent", no_argument, NULL, 's' },
+ { "rising-edge", no_argument, NULL, 'r' },
+ { "falling-edge", no_argument, NULL, 'f' },
+ { "line-buffered", no_argument, NULL, 'b' },
+ { "format", required_argument, NULL, 'F' },
+ { GETOPT_NULL_LONGOPT },
+};
+
+static const char *const shortopts = "+hvln:srfbF:";
+
+static void print_help(void)
+{
+ printf("Usage: %s [OPTIONS] <chip name/number> <offset 1> <offset 2> ...\n",
+ get_progname());
+ printf("Wait for events on GPIO lines and print them to standard output\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -h, --help:\t\tdisplay this message and exit\n");
+ printf(" -v, --version:\tdisplay the version and exit\n");
+ printf(" -l, --active-low:\tset the line active state to low\n");
+ printf(" -n, --num-events=NUM:\texit after processing NUM events\n");
+ printf(" -s, --silent:\t\tdon't print event info\n");
+ printf(" -r, --rising-edge:\tonly process rising edge events\n");
+ printf(" -f, --falling-edge:\tonly process falling edge events\n");
+ printf(" -b, --line-buffered:\tset standard output as line buffered\n");
+ printf(" -F, --format=FMT\tspecify custom output format\n");
+ printf("\n");
+ printf("Format specifiers:\n");
+ printf(" %%o: GPIO line offset\n");
+ printf(" %%e: event type (0 - falling edge, 1 rising edge)\n");
+ printf(" %%s: seconds part of the event timestamp\n");
+ printf(" %%n: nanoseconds part of the event timestamp\n");
+}
+
+struct mon_ctx {
+ unsigned int offset;
+ unsigned int events_wanted;
+ unsigned int events_done;
+
+ bool silent;
+ char *fmt;
+
+ int sigfd;
+};
+
+static void event_print_custom(unsigned int offset,
+ const struct timespec *ts,
+ int event_type,
+ struct mon_ctx *ctx)
+{
+ char *prev, *curr, fmt;
+
+ for (prev = curr = ctx->fmt;;) {
+ curr = strchr(curr, '%');
+ if (!curr) {
+ fputs(prev, stdout);
+ break;
+ }
+
+ if (prev != curr)
+ fwrite(prev, curr - prev, 1, stdout);
+
+ fmt = *(curr + 1);
+
+ switch (fmt) {
+ case 'o':
+ printf("%u", offset);
+ break;
+ case 'e':
+ if (event_type == GPIOD_CTXLESS_EVENT_CB_RISING_EDGE)
+ fputc('1', stdout);
+ else
+ fputc('0', stdout);
+ break;
+ case 's':
+ printf("%ld", ts->tv_sec);
+ break;
+ case 'n':
+ printf("%ld", ts->tv_nsec);
+ break;
+ case '%':
+ fputc('%', stdout);
+ break;
+ case '\0':
+ fputc('%', stdout);
+ goto end;
+ default:
+ fwrite(curr, 2, 1, stdout);
+ break;
+ }
+
+ curr += 2;
+ prev = curr;
+ }
+
+end:
+ fputc('\n', stdout);
+}
+
+static void event_print_human_readable(unsigned int offset,
+ const struct timespec *ts,
+ int event_type)
+{
+ char *evname;
+
+ if (event_type == GPIOD_CTXLESS_EVENT_CB_RISING_EDGE)
+ evname = " RISING EDGE";
+ else
+ evname = "FALLING EDGE";
+
+ printf("event: %s offset: %u timestamp: [%8ld.%09ld]\n",
+ evname, offset, ts->tv_sec, ts->tv_nsec);
+}
+
+static int poll_callback(unsigned int num_lines,
+ struct gpiod_ctxless_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, rv;
+ unsigned int i;
+
+ for (i = 0; i < num_lines; i++) {
+ pfds[i].fd = fds[i].fd;
+ pfds[i].events = POLLIN | POLLPRI;
+ }
+
+ pfds[i].fd = ctx->sigfd;
+ pfds[i].events = POLLIN | POLLPRI;
+
+ ts = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;
+
+ cnt = poll(pfds, num_lines + 1, ts);
+ if (cnt < 0)
+ return GPIOD_CTXLESS_EVENT_POLL_RET_ERR;
+ else if (cnt == 0)
+ return GPIOD_CTXLESS_EVENT_POLL_RET_TIMEOUT;
+
+ rv = cnt;
+ for (i = 0; i < num_lines; i++) {
+ if (pfds[i].revents) {
+ fds[i].event = true;
+ if (!--cnt)
+ return rv;
+ }
+ }
+
+ /*
+ * If we're here, then there's a signal pending. No need to read it,
+ * we know we should quit now.
+ */
+ close(ctx->sigfd);
+
+ return GPIOD_CTXLESS_EVENT_POLL_RET_STOP;
+}
+
+static void handle_event(struct mon_ctx *ctx, int event_type,
+ unsigned int line_offset,
+ const struct timespec *timestamp)
+{
+ if (!ctx->silent) {
+ if (ctx->fmt)
+ event_print_custom(line_offset, timestamp,
+ event_type, ctx);
+ else
+ event_print_human_readable(line_offset,
+ timestamp, event_type);
+ }
+
+ ctx->events_done++;
+}
+
+static int event_callback(int event_type, unsigned int line_offset,
+ const struct timespec *timestamp, void *data)
+{
+ struct mon_ctx *ctx = data;
+
+ switch (event_type) {
+ case GPIOD_CTXLESS_EVENT_CB_RISING_EDGE:
+ case GPIOD_CTXLESS_EVENT_CB_FALLING_EDGE:
+ 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;
+ }
+
+ if (ctx->events_wanted && ctx->events_done >= ctx->events_wanted)
+ return GPIOD_CTXLESS_EVENT_CB_RET_STOP;
+
+ return GPIOD_CTXLESS_EVENT_CB_RET_OK;
+}
+
+static int make_signalfd(void)
+{
+ sigset_t sigmask;
+ int sigfd, rv;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGTERM);
+ sigaddset(&sigmask, SIGINT);
+
+ rv = sigprocmask(SIG_BLOCK, &sigmask, NULL);
+ if (rv < 0)
+ die("error masking signals: %s", strerror(errno));
+
+ sigfd = signalfd(-1, &sigmask, 0);
+ if (sigfd < 0)
+ die("error creating signalfd: %s", strerror(errno));
+
+ return sigfd;
+}
+
+int main(int argc, char **argv)
+{
+ 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 };
+ int optc, opti, rv, i, event_type;
+ struct mon_ctx ctx;
+ char *end;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ for (;;) {
+ optc = getopt_long(argc, argv, shortopts, longopts, &opti);
+ if (optc < 0)
+ break;
+
+ switch (optc) {
+ case 'h':
+ print_help();
+ return EXIT_SUCCESS;
+ case 'v':
+ print_version();
+ return EXIT_SUCCESS;
+ case 'l':
+ active_low = true;
+ break;
+ case 'n':
+ ctx.events_wanted = strtoul(optarg, &end, 10);
+ if (*end != '\0')
+ die("invalid number: %s", optarg);
+ break;
+ case 's':
+ ctx.silent = true;
+ break;
+ case 'r':
+ watch_rising = true;
+ break;
+ case 'f':
+ watch_falling = true;
+ break;
+ case 'b':
+ setlinebuf(stdout);
+ break;
+ case 'F':
+ ctx.fmt = optarg;
+ break;
+ case '?':
+ die("try %s --help", get_progname());
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ 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");
+
+ if (argc < 2)
+ die("at least one GPIO line offset must be specified");
+
+ for (i = 1; i < argc; i++) {
+ offset = strtoul(argv[i], &end, 10);
+ if (*end != '\0' || offset > INT_MAX)
+ die("invalid GPIO offset: %s", argv[i]);
+
+ offsets[i - 1] = offset;
+ num_lines++;
+ }
+
+ ctx.sigfd = make_signalfd();
+
+ rv = gpiod_ctxless_event_monitor_multiple(argv[0], event_type,
+ offsets, num_lines,
+ active_low, "gpiomon",
+ &timeout, poll_callback,
+ event_callback, &ctx);
+ if (rv)
+ die_perror("error waiting for events");
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+#include <errno.h>
+#include <gpiod.h>
+#include <getopt.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/signalfd.h>
+#include <unistd.h>
+
+#include "tools-common.h"
+
+static const struct option longopts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { "active-low", no_argument, NULL, 'l' },
+ { "mode", required_argument, NULL, 'm' },
+ { "sec", required_argument, NULL, 's' },
+ { "usec", required_argument, NULL, 'u' },
+ { "background", no_argument, NULL, 'b' },
+ { GETOPT_NULL_LONGOPT },
+};
+
+static const char *const shortopts = "+hvlm:s:u:b";
+
+static void print_help(void)
+{
+ printf("Usage: %s [OPTIONS] <chip name/number> <offset1>=<value1> <offset2>=<value2> ...\n",
+ get_progname());
+ printf("Set GPIO line values of a GPIO chip and maintain the state until the process exits\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -h, --help:\t\tdisplay this message and exit\n");
+ printf(" -v, --version:\tdisplay the version and exit\n");
+ printf(" -l, --active-low:\tset the line active state to low\n");
+ printf(" -m, --mode=[exit|wait|time|signal] (defaults to 'exit'):\n");
+ printf(" tell the program what to do after setting values\n");
+ printf(" -s, --sec=SEC:\tspecify the number of seconds to wait (only valid for --mode=time)\n");
+ printf(" -u, --usec=USEC:\tspecify the number of microseconds to wait (only valid for --mode=time)\n");
+ printf(" -b, --background:\tafter setting values: detach from the controlling terminal\n");
+ printf("\n");
+ printf("Modes:\n");
+ printf(" exit:\t\tset values and exit immediately\n");
+ printf(" wait:\t\tset values and wait for user to press ENTER\n");
+ printf(" time:\t\tset values and sleep for a specified amount of time\n");
+ printf(" signal:\tset values and wait for SIGINT or SIGTERM\n");
+ printf("\n");
+ printf("Note: the state of a GPIO line controlled over the character device reverts to default\n");
+ printf("when the last process referencing the file descriptor representing the device file exits.\n");
+ printf("This means that it's wrong to run gpioset, have it exit and expect the line to continue\n");
+ printf("being driven high or low. It may happen if given pin is floating but it must be interpreted\n");
+ printf("as undefined behavior.\n");
+}
+
+struct callback_data {
+ /* Replace with a union once we have more modes using callback data. */
+ struct timeval tv;
+ bool daemonize;
+};
+
+static void maybe_daemonize(bool daemonize)
+{
+ int rv;
+
+ if (daemonize) {
+ rv = daemon(0, 0);
+ if (rv < 0)
+ die("unable to daemonize: %s", strerror(errno));
+ }
+}
+
+static void wait_enter(void *data GPIOD_UNUSED)
+{
+ getchar();
+}
+
+static void wait_time(void *data)
+{
+ struct callback_data *cbdata = data;
+
+ maybe_daemonize(cbdata->daemonize);
+ select(0, NULL, NULL, NULL, &cbdata->tv);
+}
+
+static void wait_signal(void *data)
+{
+ struct callback_data *cbdata = data;
+ struct pollfd pfd;
+ sigset_t sigmask;
+ int sigfd, rv;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGTERM);
+ sigaddset(&sigmask, SIGINT);
+
+ rv = sigprocmask(SIG_BLOCK, &sigmask, NULL);
+ if (rv < 0)
+ die("error blocking signals: %s", strerror(errno));
+
+ sigfd = signalfd(-1, &sigmask, 0);
+ if (sigfd < 0)
+ die("error creating signalfd: %s", strerror(errno));
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = sigfd;
+ pfd.events = POLLIN | POLLPRI;
+
+ maybe_daemonize(cbdata->daemonize);
+
+ for (;;) {
+ rv = poll(&pfd, 1, 1000 /* one second */);
+ if (rv < 0)
+ die("error polling for signals: %s", strerror(errno));
+ else if (rv > 0)
+ break;
+ }
+
+ /*
+ * Don't bother reading siginfo - it's enough to know that we
+ * received any signal.
+ */
+ close(sigfd);
+}
+
+enum {
+ MODE_EXIT = 0,
+ MODE_WAIT,
+ MODE_TIME,
+ MODE_SIGNAL,
+};
+
+struct mode_mapping {
+ int id;
+ const char *name;
+ gpiod_ctxless_set_value_cb callback;
+};
+
+static const struct mode_mapping modes[] = {
+ [MODE_EXIT] = {
+ .id = MODE_EXIT,
+ .name = "exit",
+ .callback = NULL,
+ },
+ [MODE_WAIT] = {
+ .id = MODE_WAIT,
+ .name = "wait",
+ .callback = wait_enter,
+ },
+ [MODE_TIME] = {
+ .id = MODE_TIME,
+ .name = "time",
+ .callback = wait_time,
+ },
+ [MODE_SIGNAL] = {
+ .id = MODE_SIGNAL,
+ .name = "signal",
+ .callback = wait_signal,
+ },
+};
+
+static const struct mode_mapping *parse_mode(const char *mode)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(modes); i++)
+ if (strcmp(mode, modes[i].name) == 0)
+ return &modes[i];
+
+ return NULL;
+}
+
+int main(int argc, char **argv)
+{
+ const struct mode_mapping *mode = &modes[MODE_EXIT];
+ unsigned int *offsets, num_lines, i;
+ int *values, rv, optc, opti;
+ struct callback_data cbdata;
+ bool active_low = false;
+ char *device, *end;
+
+ memset(&cbdata, 0, sizeof(cbdata));
+
+ for (;;) {
+ optc = getopt_long(argc, argv, shortopts, longopts, &opti);
+ if (optc < 0)
+ break;
+
+ switch (optc) {
+ case 'h':
+ print_help();
+ return EXIT_SUCCESS;
+ case 'v':
+ print_version();
+ return EXIT_SUCCESS;
+ case 'l':
+ active_low = true;
+ break;
+ case 'm':
+ mode = parse_mode(optarg);
+ if (!mode)
+ die("invalid mode: %s", optarg);
+ break;
+ case 's':
+ cbdata.tv.tv_sec = strtoul(optarg, &end, 10);
+ if (*end != '\0')
+ die("invalid time value in seconds: %s", optarg);
+ break;
+ case 'u':
+ cbdata.tv.tv_usec = strtoul(optarg, &end, 10);
+ if (*end != '\0')
+ die("invalid time value in microseconds: %s",
+ optarg);
+ break;
+ case 'b':
+ cbdata.daemonize = true;
+ break;
+ case '?':
+ die("try %s --help", get_progname());
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (mode->id != MODE_TIME && (cbdata.tv.tv_sec || cbdata.tv.tv_usec))
+ die("can't specify wait time in this mode");
+
+ if (mode->id != MODE_SIGNAL &&
+ mode->id != MODE_TIME &&
+ cbdata.daemonize)
+ die("can't daemonize in this mode");
+
+ if (argc < 1)
+ die("gpiochip must be specified");
+
+ if (argc < 2)
+ die("at least one GPIO line offset to value mapping must be specified");
+
+ device = argv[0];
+
+ num_lines = argc - 1;
+
+ offsets = malloc(sizeof(*offsets) * num_lines);
+ values = malloc(sizeof(*values) * num_lines);
+ if (!values || !offsets)
+ die("out of memory");
+
+ for (i = 0; i < num_lines; i++) {
+ rv = sscanf(argv[i + 1], "%u=%d", &offsets[i], &values[i]);
+ if (rv != 2)
+ die("invalid offset<->value mapping: %s", argv[i + 1]);
+
+ if (values[i] != 0 && values[i] != 1)
+ die("value must be 0 or 1: %s", argv[i + 1]);
+
+ if (offsets[i] > INT_MAX)
+ die("invalid offset: %s", argv[i + 1]);
+ }
+
+ rv = gpiod_ctxless_set_value_multiple(device, offsets, values,
+ num_lines, active_low, "gpioset",
+ mode->callback, &cbdata);
+ if (rv < 0)
+ die_perror("error setting the GPIO line values");
+
+ free(offsets);
+ free(values);
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+/* Common code for GPIO tools. */
+
+#include <errno.h>
+#include <gpiod.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tools-common.h"
+
+const char *get_progname(void)
+{
+ return program_invocation_name;
+}
+
+void die(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ fprintf(stderr, "%s: ", program_invocation_name);
+ vfprintf(stderr, fmt, va);
+ fprintf(stderr, "\n");
+ va_end(va);
+
+ exit(EXIT_FAILURE);
+}
+
+void die_perror(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ fprintf(stderr, "%s: ", program_invocation_name);
+ vfprintf(stderr, fmt, va);
+ fprintf(stderr, ": %s\n", strerror(errno));
+ va_end(va);
+
+ exit(EXIT_FAILURE);
+}
+
+void print_version(void)
+{
+ printf("%s (libgpiod) v%s\n",
+ program_invocation_short_name, gpiod_version_string());
+ printf("Copyright (C) 2017-2018 Bartosz Golaszewski\n");
+ printf("License: LGPLv2.1\n");
+ printf("This is free software: you are free to change and redistribute it.\n");
+ printf("There is NO WARRANTY, to the extent permitted by law.\n");
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ */
+
+#ifndef __GPIOD_TOOLS_COMMON_H__
+#define __GPIOD_TOOLS_COMMON_H__
+
+/*
+ * Various helpers for the GPIO tools.
+ *
+ * NOTE: This is not a stable interface - it's only to avoid duplicating
+ * common code.
+ */
+
+#define NORETURN __attribute__((noreturn))
+#define PRINTF(fmt, arg) __attribute__((format(printf, fmt, arg)))
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+
+#define GETOPT_NULL_LONGOPT NULL, 0, NULL, 0
+
+const char *get_progname(void);
+void die(const char *fmt, ...) NORETURN PRINTF(1, 2);
+void die_perror(const char *fmt, ...) NORETURN PRINTF(1, 2);
+void print_version(void);
+
+#endif /* __GPIOD_TOOLS_COMMON_H__ */