From: Bartosz Golaszewski Date: Sun, 15 Jan 2017 17:24:24 +0000 (+0100) Subject: build: create an organized directory structure X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=06cb40722aaf87d7ff7b7d168114191cfa33b312;p=qemu-gpiodev%2Flibgpiod.git build: create an organized directory structure Signed-off-by: Bartosz Golaszewski --- diff --git a/Makefile.am b/Makefile.am index fa2c23a..6151e59 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,47 +7,8 @@ # AUTOMAKE_OPTIONS = foreign -LIBS = - -AM_CPPFLAGS = -I$(top_srcdir) -include $(top_srcdir)/config.h -Wextra -AM_CPPFLAGS += -fvisibility=hidden -D_GNU_SOURCE - -include_HEADERS = gpiod.h - -lib_LTLIBRARIES = libgpiod.la -libgpiod_la_SOURCES = core.c -libgpiod_la_CFLAGS = -g -libgpiod_la_LDFLAGS = -version-number $(subst .,:,$(PACKAGE_VERSION)) - -if WITH_TOOLS - -bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind - -gpiodetect_SOURCES = gpiodetect.c tools-common.c -gpiodetect_LDFLAGS = -lgpiod -gpiodetect_DEPENDENCIES = libgpiod.la - -gpioinfo_SOURCES = gpioinfo.c tools-common.c -gpioinfo_LDFLAGS = -lgpiod -gpioinfo_DEPENDENCIES = libgpiod.la - -gpioget_SOURCES = gpioget.c tools-common.c -gpioget_LDFLAGS = -lgpiod -gpioget_DEPENDENCIES = libgpiod.la - -gpioset_SOURCES = gpioset.c tools-common.c -gpioset_LDFLAGS = -lgpiod -gpioset_DEPENDENCIES = libgpiod.la - -gpiomon_SOURCES = gpiomon.c tools-common.c -gpiomon_LDFLAGS = -lgpiod -gpiomon_DEPENDENCIES = libgpiod.la - -gpiofind_SOURCES = gpiofind.c tools-common.c -gpiofind_LDFLAGS = -lgpiod -gpiofind_DEPENDENCIES = libgpiod.la - -endif +ACLOCAL_AMFLAGS = -I m4 +SUBDIRS = include src doc: @(cat Doxyfile; echo PROJECT_NUMBER = $(PACKAGE_VERSION)) | doxygen - diff --git a/configure.ac b/configure.ac index 5420682..7520eb1 100644 --- a/configure.ac +++ b/configure.ac @@ -9,11 +9,13 @@ AC_PREREQ(2.61) AC_INIT([libgpiod], 0.0.0) AC_CONFIG_AUX_DIR([autostuff]) -AM_INIT_AUTOMAKE([foreign -Wall -Werror subdir-objects]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AC_CONFIG_SRCDIR([src]) AC_CONFIG_HEADER([config.h]) +AC_CONFIG_MACRO_DIR([m4]) AC_ARG_ENABLE([tools], [AC_HELP_STRING([--enable-tools], @@ -29,6 +31,7 @@ AC_ARG_ENABLE([tools], [with_tools = false]) AM_CONDITIONAL([WITH_TOOLS], [test x$with_tools = xtrue]) +AM_PROG_AR AC_PROG_CC AC_PROG_LIBTOOL AC_PROG_INSTALL @@ -49,8 +52,10 @@ AC_CHECK_HEADERS([dirent.h], [], [AC_MSG_ERROR([dirent.h header not found])]) AC_CHECK_HEADERS([linux/gpio.h], [], [AC_MSG_ERROR([linux/gpio.h header not found])]) -AC_CONFIG_FILES([Makefile]) - -CFLAGS="$CFLAGS -Wall" +AC_CONFIG_FILES([Makefile + include/Makefile + src/Makefile + src/lib/Makefile + src/tools/Makefile]) AC_OUTPUT diff --git a/core.c b/core.c deleted file mode 100644 index ddd19ae..0000000 --- a/core.c +++ /dev/null @@ -1,1084 +0,0 @@ -/* - * GPIO chardev utils for linux. - * - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - */ - -#include "gpiod.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct gpiod_chip -{ - int fd; - struct gpiochip_info cinfo; - struct gpiod_line *lines; -}; - -enum { - LINE_FREE = 0, - LINE_TAKEN, - LINE_EVENT, -}; - -struct handle_data { - struct gpiohandle_request request; - int refcount; -}; - -struct gpiod_line { - int state; - bool up_to_date; - struct gpiod_chip *chip; - struct gpioline_info info; - union { - struct handle_data *handle; - struct gpioevent_request event; - }; -}; - -enum { - CHIP_ITER_INIT = 0, - CHIP_ITER_DONE, - CHIP_ITER_ERR, -}; - -struct gpiod_chip_iter -{ - DIR *dir; - struct gpiod_chip *current; - int state; - char *failed_chip; -}; - -static const char dev_dir[] = "/dev/"; -static const char cdev_prefix[] = "gpiochip"; -static const char libgpiod_consumer[] = "libgpiod"; - -/* - * The longest error message in glibc is about 50 characters long so 64 should - * be enough to store every error message in the future too. - */ -#define ERRSTR_MAX 64 - -static __thread int last_error; -static __thread char errmsg[ERRSTR_MAX]; - -static const char *const error_descr[] = { - "success", - "GPIO line not reserved", - "no events configured on GPIO line", - "GPIO lines in bulk don't belong to the same gpiochip", - "GPIO line currently in use", -}; - -static void set_last_error(int errnum) -{ - last_error = errnum; -} - -static void last_error_from_errno(void) -{ - last_error = errno; -} - -static void * zalloc(size_t size) -{ - void *ptr; - - ptr = malloc(size); - if (!ptr) { - set_last_error(ENOMEM); - return NULL; - } - - memset(ptr, 0, size); - - return ptr; -} - -static bool is_unsigned_int(const char *str) -{ - for (; *str && isdigit(*str); str++); - - return *str == '\0'; -} - -static void nsec_to_timespec(uint64_t nsec, struct timespec *ts) -{ - ts->tv_sec = nsec / 1000000000ULL; - ts->tv_nsec = (nsec % 1000000000ULL); -} - -static int gpio_ioctl(int fd, unsigned long request, void *data) -{ - int status; - - status = ioctl(fd, request, data); - if (status < 0) { - last_error_from_errno(); - return -1; - } - - return 0; -} - -int gpiod_errno(void) -{ - return last_error; -} - -const char * gpiod_strerror(int errnum) -{ - if (errnum < __GPIOD_ERRNO_OFFSET) - return strerror_r(errnum, errmsg, sizeof(errmsg)); - else if (errnum > __GPIOD_MAX_ERR) - return "invalid error number"; - else - return error_descr[errnum - __GPIOD_ERRNO_OFFSET]; -} - -const char * gpiod_last_strerror(void) -{ - return gpiod_strerror(gpiod_errno()); -} - -int gpiod_simple_get_value(const char *device, - unsigned int offset, bool active_low) -{ - struct gpiod_chip *chip; - struct gpiod_line *line; - int status, value; - - chip = gpiod_chip_open_lookup(device); - if (!chip) - return -1; - - line = gpiod_chip_get_line(chip, offset); - if (!line) { - gpiod_chip_close(chip); - return -1; - } - - status = gpiod_line_request_input(line, libgpiod_consumer, active_low); - if (status < 0) { - gpiod_chip_close(chip); - return -1; - } - - value = gpiod_line_get_value(line); - - gpiod_line_release(line); - gpiod_chip_close(chip); - - return value; -} - -int gpiod_simple_set_value(const char *device, unsigned int offset, int value, - bool active_low, void (*cb)(void *), void *data) -{ - struct gpiod_chip *chip; - struct gpiod_line *line; - int status; - - chip = gpiod_chip_open_lookup(device); - if (!chip) - return -1; - - line = gpiod_chip_get_line(chip, offset); - if (!line) { - gpiod_chip_close(chip); - return -1; - } - - status = gpiod_line_request_output(line, libgpiod_consumer, - active_low, value); - if (status < 0) { - gpiod_chip_close(chip); - return -1; - } - - if (cb) - cb(data); - - gpiod_line_release(line); - gpiod_chip_close(chip); - - return 0; -} - -int gpiod_simple_event_loop(const char *device, unsigned int offset, - bool active_low, struct timespec *timeout, - gpiod_event_cb callback, void *cbdata) -{ - struct gpiod_line_event event; - struct gpiod_chip *chip; - struct gpiod_line *line; - int status, evtype; - - chip = gpiod_chip_open_lookup(device); - if (!chip) - return -1; - - line = gpiod_chip_get_line(chip, offset); - if (!line) { - gpiod_chip_close(chip); - return -1; - } - - status = gpiod_line_event_request_all(line, - libgpiod_consumer, active_low); - if (status < 0) { - gpiod_chip_close(chip); - return -1; - } - - for (;;) { - status = gpiod_line_event_wait(line, timeout); - if (status < 0) { - if (gpiod_errno() == EINTR) - return evtype = GPIOD_EVENT_CB_TIMEOUT; - else - goto out; - } else if (status == 0) { - evtype = GPIOD_EVENT_CB_TIMEOUT; - } else { - status = gpiod_line_event_read(line, &event); - if (status < 0) - goto out; - - evtype = event.event_type == GPIOD_EVENT_RISING_EDGE - ? GPIOD_EVENT_CB_RISING_EDGE - : GPIOD_EVENT_CB_FALLING_EDGE; - } - - status = callback(evtype, &event.ts, cbdata); - if (status == GPIOD_EVENT_CB_STOP) - goto out; - } - -out: - gpiod_line_event_release(line); - gpiod_chip_close(chip); - - return status; -} - -static void line_set_offset(struct gpiod_line *line, unsigned int offset) -{ - line->info.line_offset = offset; -} - -static int line_get_state(struct gpiod_line *line) -{ - return line->state; -} - -static void line_set_state(struct gpiod_line *line, int state) -{ - line->state = state; -} - -static int line_get_handle_fd(struct gpiod_line *line) -{ - if (line_get_state(line) != LINE_TAKEN) - return -1; - else - return line->handle->request.fd; -} - -static int line_get_event_fd(struct gpiod_line *line) -{ - if (line_get_state(line) != LINE_EVENT) - return -1; - else - return line->event.fd; -} - -static void line_set_handle(struct gpiod_line *line, - struct handle_data *handle) -{ - line->handle = handle; - handle->refcount++; -} - -static void line_remove_handle(struct gpiod_line *line) -{ - struct handle_data *handle; - - if (!line->handle) - return; - - handle = line->handle; - line->handle = NULL; - handle->refcount--; - if (handle->refcount <= 0) { - close(handle->request.fd); - free(handle); - } -} - -unsigned int gpiod_line_offset(struct gpiod_line *line) -{ - return (unsigned int)line->info.line_offset; -} - -const char * gpiod_line_name(struct gpiod_line *line) -{ - return line->info.name[0] == '\0' ? NULL : line->info.name; -} - -const char * gpiod_line_consumer(struct gpiod_line *line) -{ - return line->info.consumer[0] == '\0' ? NULL : line->info.consumer; -} - -int gpiod_line_direction(struct gpiod_line *line) -{ - return line->info.flags & GPIOLINE_FLAG_IS_OUT ? GPIOD_DIRECTION_OUTPUT - : GPIOD_DIRECTION_INPUT; -} - -int gpiod_line_active_state(struct gpiod_line *line) -{ - return line->info.flags & GPIOLINE_FLAG_ACTIVE_LOW - ? GPIOD_ACTIVE_STATE_LOW - : GPIOD_ACTIVE_STATE_HIGH; -} - -bool gpiod_line_is_used_by_kernel(struct gpiod_line *line) -{ - return line->info.flags & GPIOLINE_FLAG_KERNEL; -} - -bool gpiod_line_is_open_drain(struct gpiod_line *line) -{ - return line->info.flags & GPIOLINE_FLAG_OPEN_DRAIN; -} - -bool gpiod_line_is_open_source(struct gpiod_line *line) -{ - return line->info.flags & GPIOLINE_FLAG_OPEN_SOURCE; -} - -static void line_set_updated(struct gpiod_line *line) -{ - line->up_to_date = true; -} - -static void line_set_needs_update(struct gpiod_line *line) -{ - line->up_to_date = false; -} - -static void line_update(struct gpiod_line *line) -{ - int status; - - status = gpiod_line_update(line); - if (status < 0) - line_set_needs_update(line); -} - -bool gpiod_line_needs_update(struct gpiod_line *line) -{ - return !line->up_to_date; -} - -int gpiod_line_update(struct gpiod_line *line) -{ - struct gpiod_chip *chip; - int status, fd; - - memset(line->info.name, 0, sizeof(line->info.name)); - memset(line->info.consumer, 0, sizeof(line->info.consumer)); - line->info.flags = 0; - - chip = gpiod_line_get_chip(line); - fd = chip->fd; - - status = gpio_ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line->info); - if (status < 0) - return -1; - - line_set_updated(line); - - 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 verify_line_bulk(struct gpiod_line_bulk *line_bulk) -{ - struct gpiod_chip *chip; - unsigned int i; - - chip = gpiod_line_get_chip(line_bulk->lines[0]); - - for (i = 1; i < line_bulk->num_lines; i++) { - if (chip != gpiod_line_get_chip(line_bulk->lines[i])) { - set_last_error(GPIOD_EBULKINCOH); - return false; - } - - if (!gpiod_line_is_free(line_bulk->lines[i])) { - set_last_error(GPIOD_ELINEBUSY); - return false; - } - } - - return true; -} - -int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk, - const struct gpiod_line_request_config *config, - int *default_vals) -{ - struct gpiohandle_request *req; - struct handle_data *handle; - struct gpiod_chip *chip; - struct gpiod_line *line; - int status, fd; - unsigned int i; - - if (!verify_line_bulk(bulk)) - return -1; - - handle = zalloc(sizeof(*handle)); - if (!handle) - return -1; - - req = &handle->request; - - if (config->flags & GPIOD_REQUEST_OPEN_DRAIN) - req->flags |= GPIOHANDLE_REQUEST_OPEN_DRAIN; - if (config->flags & GPIOD_REQUEST_OPEN_SOURCE) - req->flags |= GPIOHANDLE_REQUEST_OPEN_SOURCE; - - if (config->direction == GPIOD_DIRECTION_INPUT) - req->flags |= GPIOHANDLE_REQUEST_INPUT; - else if (config->direction == GPIOD_DIRECTION_OUTPUT) - req->flags |= GPIOHANDLE_REQUEST_OUTPUT; - - if (config->active_state == GPIOD_ACTIVE_STATE_LOW) - req->flags |= GPIOHANDLE_REQUEST_ACTIVE_LOW; - - req->lines = bulk->num_lines; - - for (i = 0; i < bulk->num_lines; i++) { - req->lineoffsets[i] = gpiod_line_offset(bulk->lines[i]); - if (config->direction == GPIOD_DIRECTION_OUTPUT) - req->default_values[i] = !!default_vals[i]; - } - - strncpy(req->consumer_label, config->consumer, - sizeof(req->consumer_label) - 1); - - chip = gpiod_line_get_chip(bulk->lines[0]); - fd = chip->fd; - - status = gpio_ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, req); - if (status < 0) - return -1; - - for (i = 0; i < bulk->num_lines; i++) { - line = bulk->lines[i]; - - line_set_handle(line, handle); - line_set_state(line, LINE_TAKEN); - line_update(line); - } - - return 0; -} - -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; - unsigned int i; - - for (i = 0; i < bulk->num_lines; i++) { - line = bulk->lines[i]; - - line_remove_handle(line); - line_set_state(line, LINE_FREE); - line_update(line); - } -} - -bool gpiod_line_is_reserved(struct gpiod_line *line) -{ - return line_get_state(line) == LINE_TAKEN; -} - -bool gpiod_line_is_free(struct gpiod_line *line) -{ - return line_get_state(line) == LINE_FREE; -} - -static bool line_bulk_is_reserved(struct gpiod_line_bulk *line_bulk) -{ - unsigned int i; - - for (i = 0; i < line_bulk->num_lines; i++) { - if (!gpiod_line_is_reserved(line_bulk->lines[i])) - return false; - } - - return true; -} - -int gpiod_line_get_value(struct gpiod_line *line) -{ - struct gpiod_line_bulk bulk; - int status, value; - - gpiod_line_bulk_init(&bulk); - gpiod_line_bulk_add(&bulk, line); - - status = gpiod_line_get_value_bulk(&bulk, &value); - if (status < 0) - return -1; - - return value; -} - -int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, int *values) -{ - struct gpiohandle_data data; - unsigned int i; - int status; - - if (!line_bulk_is_reserved(bulk)) { - set_last_error(GPIOD_EREQUEST); - return -1; - } - - memset(&data, 0, sizeof(data)); - - status = gpio_ioctl(line_get_handle_fd(bulk->lines[0]), - GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); - if (status < 0) - return -1; - - for (i = 0; i < bulk->num_lines; 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, int *values) -{ - struct gpiohandle_data data; - unsigned int i; - int status; - - if (!line_bulk_is_reserved(bulk)) { - set_last_error(GPIOD_EREQUEST); - return -1; - } - - memset(&data, 0, sizeof(data)); - - for (i = 0; i < bulk->num_lines; i++) - data.values[i] = (__u8)!!values[i]; - - status = gpio_ioctl(line_get_handle_fd(bulk->lines[0]), - GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data); - if (status < 0) - return -1; - - return 0; -} - -struct gpiod_line * gpiod_line_find_by_name(const char *name) -{ - struct gpiod_chip_iter *chip_iter; - struct gpiod_line_iter line_iter; - struct gpiod_chip *chip; - struct gpiod_line *line; - const char *line_name; - - chip_iter = gpiod_chip_iter_new(); - if (!chip_iter) - return NULL; - - gpiod_foreach_chip(chip_iter, chip) { - gpiod_line_iter_init(&line_iter, chip); - gpiod_foreach_line(&line_iter, line) { - line_name = gpiod_line_name(line); - if (!line_name) - continue; - - if (strcmp(gpiod_line_name(line), name) == 0) { - gpiod_chip_iter_free_noclose(chip_iter); - return line; - } - } - } - - gpiod_chip_iter_free(chip_iter); - - return NULL; -} - -int gpiod_line_event_request(struct gpiod_line *line, - struct gpiod_line_evreq_config *config) -{ - struct gpioevent_request *req; - struct gpiod_chip *chip; - int status, fd; - - if (!gpiod_line_is_free(line)) { - set_last_error(GPIOD_ELINEBUSY); - return -1; - } - - req = &line->event; - - memset(req, 0, sizeof(*req)); - strncpy(req->consumer_label, config->consumer, - sizeof(req->consumer_label) - 1); - req->lineoffset = gpiod_line_offset(line); - req->handleflags |= GPIOHANDLE_REQUEST_INPUT; - - if (config->line_flags & GPIOD_REQUEST_OPEN_DRAIN) - req->handleflags |= GPIOHANDLE_REQUEST_OPEN_DRAIN; - if (config->line_flags & GPIOD_REQUEST_OPEN_SOURCE) - req->handleflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE; - - if (config->active_state == GPIOD_ACTIVE_STATE_LOW) - req->handleflags |= GPIOHANDLE_REQUEST_ACTIVE_LOW; - - if (config->event_type == GPIOD_EVENT_RISING_EDGE) - req->eventflags |= GPIOEVENT_EVENT_RISING_EDGE; - else if (config->event_type == GPIOD_EVENT_FALLING_EDGE) - req->eventflags |= GPIOEVENT_EVENT_FALLING_EDGE; - else if (req->eventflags |= GPIOD_EVENT_BOTH_EDGES) - req->eventflags |= GPIOEVENT_REQUEST_BOTH_EDGES; - - chip = gpiod_line_get_chip(line); - fd = chip->fd; - - status = gpio_ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, req); - if (status < 0) - return -1; - - line_set_state(line, LINE_EVENT); - - return 0; -} - -void gpiod_line_event_release(struct gpiod_line *line) -{ - close(line_get_event_fd(line)); - line_set_state(line, LINE_FREE); -} - -bool gpiod_line_event_configured(struct gpiod_line *line) -{ - return line_get_state(line) == LINE_EVENT; -} - -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); -} - -static bool line_bulk_is_event_configured(struct gpiod_line_bulk *line_bulk) -{ - unsigned int i; - - for (i = 0; i < line_bulk->num_lines; i++) { - if (!gpiod_line_event_configured(line_bulk->lines[i])) - return false; - } - - return true; -} - -int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk, - const struct timespec *timeout, - struct gpiod_line **line) -{ - struct pollfd fds[GPIOD_REQUEST_MAX_LINES]; - struct gpiod_line *linetmp; - unsigned int i; - int status; - - if (!line_bulk_is_event_configured(bulk)) { - set_last_error(GPIOD_EEVREQUEST); - return -1; - } - - memset(fds, 0, sizeof(fds)); - - for (i = 0; i < bulk->num_lines; i++) { - linetmp = bulk->lines[i]; - - fds[i].fd = line_get_event_fd(linetmp); - fds[i].events = POLLIN | POLLPRI; - } - - status = ppoll(fds, bulk->num_lines, timeout, NULL); - if (status < 0) { - last_error_from_errno(); - return -1; - } else if (status == 0) { - return 0; - } - - for (i = 0; !fds[i].revents; i++); - if (line) - *line = bulk->lines[i]; - - return 1; -} - -int gpiod_line_event_read(struct gpiod_line *line, - struct gpiod_line_event *event) -{ - int fd; - - if (!gpiod_line_event_configured(line)) { - set_last_error(GPIOD_EEVREQUEST); - return -1; - } - - fd = line_get_event_fd(line); - - return gpiod_line_event_read_fd(fd, event); -} - -int gpiod_line_event_get_fd(struct gpiod_line *line) -{ - return line_get_state(line) == LINE_EVENT - ? line_get_event_fd(line) : -1; -} - -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) { - last_error_from_errno(); - return -1; - } else if (rd != sizeof(evdata)) { - set_last_error(EIO); - return -1; - } - - event->event_type = evdata.id == GPIOEVENT_EVENT_RISING_EDGE - ? GPIOD_EVENT_RISING_EDGE - : GPIOD_EVENT_FALLING_EDGE; - nsec_to_timespec(evdata.timestamp, &event->ts); - - return 0; -} - -struct gpiod_chip * gpiod_chip_open(const char *path) -{ - struct gpiod_chip *chip; - int status, fd; - - fd = open(path, O_RDWR); - if (fd < 0) { - last_error_from_errno(); - return NULL; - } - - chip = zalloc(sizeof(*chip)); - if (!chip) { - close(fd); - return NULL; - } - - chip->fd = fd; - - status = gpio_ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip->cinfo); - if (status < 0) { - close(chip->fd); - free(chip); - return NULL; - } - - chip->lines = zalloc(chip->cinfo.lines * sizeof(*chip->lines)); - if (!chip->lines) { - close(chip->fd); - free(chip); - return NULL; - } - - return chip; -} - -struct gpiod_chip * gpiod_chip_open_by_name(const char *name) -{ - struct gpiod_chip *chip; - char *path; - int status; - - status = asprintf(&path, "%s%s", dev_dir, name); - if (status < 0) { - last_error_from_errno(); - 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 status; - - status = asprintf(&path, "%s%s%u", dev_dir, cdev_prefix, num); - if (!status) { - last_error_from_errno(); - return NULL; - } - - chip = gpiod_chip_open(path); - free(path); - - return chip; -} - -struct gpiod_chip * gpiod_chip_open_lookup(const char *descr) -{ - if (is_unsigned_int(descr)) - return gpiod_chip_open_by_number(strtoul(descr, NULL, 10)); - else if (strncmp(descr, dev_dir, sizeof(dev_dir) - 1) != 0) - return gpiod_chip_open_by_name(descr); - else - return gpiod_chip_open(descr); -} - -void gpiod_chip_close(struct gpiod_chip *chip) -{ - unsigned int i; - - for (i = 0; i < chip->cinfo.lines; i++) { - if (chip->lines[i].state == LINE_TAKEN) - gpiod_line_release(&chip->lines[i]); - else if (chip->lines[i].state == LINE_EVENT) - gpiod_line_event_release(&chip->lines[i]); - } - - close(chip->fd); - free(chip->lines); - free(chip); -} - -const char * gpiod_chip_name(struct gpiod_chip *chip) -{ - return chip->cinfo.name[0] == '\0' ? NULL : chip->cinfo.name; -} - -const char * gpiod_chip_label(struct gpiod_chip *chip) -{ - return chip->cinfo.label[0] == '\0' ? NULL : chip->cinfo.label; -} - -unsigned int gpiod_chip_num_lines(struct gpiod_chip *chip) -{ - return (unsigned int)chip->cinfo.lines; -} - -struct gpiod_line * -gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset) -{ - struct gpiod_line *line; - int status; - - if (offset >= chip->cinfo.lines) { - set_last_error(EINVAL); - return NULL; - } - - line = &chip->lines[offset]; - line_set_offset(line, offset); - line->chip = chip; - - status = gpiod_line_update(line); - if (status < 0) - return NULL; - - return line; -} - -struct gpiod_chip * gpiod_line_get_chip(struct gpiod_line *line) -{ - return line->chip; -} - -struct gpiod_chip_iter * gpiod_chip_iter_new(void) -{ - struct gpiod_chip_iter *new; - - new = zalloc(sizeof(*new)); - if (!new) - return NULL; - - new->dir = opendir(dev_dir); - if (!new->dir) { - last_error_from_errno(); - return NULL; - } - - new->state = CHIP_ITER_INIT; - - return new; -} - -void gpiod_chip_iter_free(struct gpiod_chip_iter *iter) -{ - if (iter->current) - gpiod_chip_close(iter->current); - - gpiod_chip_iter_free_noclose(iter); -} - -void gpiod_chip_iter_free_noclose(struct gpiod_chip_iter *iter) -{ - closedir(iter->dir); - if (iter->failed_chip) - free(iter->failed_chip); - free(iter); -} - -struct gpiod_chip * gpiod_chip_iter_next(struct gpiod_chip_iter *iter) -{ - struct gpiod_chip *chip; - struct dirent *dentry; - int status; - - if (iter->current) { - gpiod_chip_close(iter->current); - iter->current = NULL; - } - - for (dentry = readdir(iter->dir); - dentry; - dentry = readdir(iter->dir)) { - status = strncmp(dentry->d_name, cdev_prefix, - sizeof(cdev_prefix) - 1); - if (status == 0) { - iter->state = CHIP_ITER_INIT; - if (iter->failed_chip) { - free(iter->failed_chip); - iter->failed_chip = NULL; - } - - chip = gpiod_chip_open_by_name(dentry->d_name); - if (!chip) { - iter->state = CHIP_ITER_ERR; - iter->failed_chip = strdup(dentry->d_name); - /* No point in an error check here. */ - } - - iter->current = chip; - return iter->current; - } - } - - iter->state = CHIP_ITER_DONE; - return NULL; -} - -bool gpiod_chip_iter_done(struct gpiod_chip_iter *iter) -{ - return iter->state == CHIP_ITER_DONE; -} - -bool gpiod_chip_iter_iserr(struct gpiod_chip_iter *iter) -{ - return iter->state == CHIP_ITER_ERR; -} - -const char * -gpiod_chip_iter_failed_chip(struct gpiod_chip_iter *iter) -{ - return iter->failed_chip; -} - -struct gpiod_line * gpiod_line_iter_next(struct gpiod_line_iter *iter) -{ - struct gpiod_line *line; - - if (iter->offset >= gpiod_chip_num_lines(iter->chip)) { - iter->state = GPIOD_LINE_ITER_DONE; - return NULL; - } - - iter->state = GPIOD_LINE_ITER_INIT; - line = gpiod_chip_get_line(iter->chip, iter->offset++); - if (!line) - iter->state = GPIOD_LINE_ITER_ERR; - - return line; -} - -const char * gpiod_version_string(void) -{ - return PACKAGE_VERSION; -} diff --git a/gpiod.h b/gpiod.h deleted file mode 100644 index f6cfefd..0000000 --- a/gpiod.h +++ /dev/null @@ -1,1029 +0,0 @@ -/* - * GPIO chardev utils for linux. - * - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - */ - -/** - * @mainpage libgpiod public API - * - * This is the documentation of the public API exported by libgpiod. - * - *

These functions and data structures allow to use all the functionalities - * exposed by the linux GPIO character device interface. - */ - -#ifndef __LIBGPIOD_GPIOD_H__ -#define __LIBGPIOD_GPIOD_H__ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct gpiod_chip; -struct gpiod_line; -struct gpiod_chip_iter; - -/** - * @defgroup __common__ Common helper macros - * @{ - * - * Commonly used utility macros. - */ - -/** - * @brief Makes symbol visible. - */ -#define GPIOD_API __attribute__((visibility("default"))) - -/** - * @brief Shift 1 by given offset. - * @param nr Bit position. - * @return 1 shifted by nr. - */ -#define GPIOD_BIT(nr) (1UL << (nr)) - -/** - * @} - * - * @defgroup __error__ Error handling - * @{ - * - * Error handling functions and macros. This library uses a combination of - * system-wide errno numbers and internal GPIO-specific errors. The routines - * in this section should be used to access the information about any error - * conditions that may occur. - * - * With some noted exceptions, all libgpiod functions set the last error - * variable if an error occurs. Internally, the last_error variable has a - * separate instance per thread making the library thread-safe. - */ - -/** - * @brief Private: offset for all libgpiod error numbers. - */ -#define __GPIOD_ERRNO_OFFSET 10000 - -/** - * @brief libgpiod-specific error numbers. - */ -enum { - GPIOD_ESUCCESS = __GPIOD_ERRNO_OFFSET, - /**< No error. */ - GPIOD_EREQUEST, - /**< The caller has no ownership of this line. */ - GPIOD_EEVREQUEST, - /**< The caller has not configured any events on this line. */ - GPIOD_EBULKINCOH, - /**< Not all lines in bulk belong to the same GPIO chip. */ - GPIOD_ELINEBUSY, - /**< This line is currently in use. */ - __GPIOD_MAX_ERR, - /**< Private: number of libgpiod-specific error numbers. */ -}; - -/** - * @brief Return last error. - * @return Number of the last error inside libgpiod. - */ -int gpiod_errno(void) GPIOD_API; - -/** - * @brief Convert error number to a human-readable string. - * @param errnum Error number to convert. - * @return Pointer to a null-terminated error description. - */ -const char * gpiod_strerror(int errnum) GPIOD_API; - -/** - * @brief Convert the last libgpiod error number to a human-readable string. - * @return Pointer to a null-terminated error description. - */ -const char * gpiod_last_strerror(void) GPIOD_API; - -/** - * @} - * - * @defgroup __high_level__ High-level API - * @{ - * - * Simple high-level routines for straightforward GPIO manipulation. - */ - -/** - * @brief Read current value from a single GPIO line. - * @param device Name, path or number of the gpiochip. - * @param offset GPIO line offset on the chip. - * @param active_low The active state of this line - true if low. - * @return 0 or 1 if the operation succeeds. On error this routine returns -1 - * and sets the last error number. - */ -int gpiod_simple_get_value(const char *device, - unsigned int offset, bool active_low) GPIOD_API; - -/** - * @brief Set value of a single GPIO line. - * @param device Name, path or number of the gpiochip. - * @param offset GPIO line offset on the chip. - * @param value New value. - * @param active_low The active state of this line - true if low. - * @param cb Callback function that will be called right after the value is - * set. Users can use this, for example, to pause the execution after - * toggling a GPIO. - * @param data User data that will be passed to the callback function. - * @return 0 or 1 if the operation succeeds. On error this routine returns -1 - * and sets the last error number. - */ -int gpiod_simple_set_value(const char *device, unsigned int offset, - int value, bool active_low, void (*cb)(void *), - void *data) GPIOD_API; - -/** - * @brief Event types that can be passed to the simple event callback. - */ -enum { - GPIOD_EVENT_CB_TIMEOUT, - /**< Waiting for events timed out. */ - GPIOD_EVENT_CB_RISING_EDGE, - /**< Rising edge event occured. */ - GPIOD_EVENT_CB_FALLING_EDGE, - /**< Falling edge event occured. */ -}; - -/** - * @brief Return status values that the simple event callback can return. - */ -enum { - GPIOD_EVENT_CB_OK = 0, - /**< Continue processing events. */ - GPIOD_EVENT_CB_STOP, - /**< Stop processing events. */ -}; - -/** - * @brief Simple event callack signature. - */ -typedef int (*gpiod_event_cb)(int, const struct timespec *, void *); - -/** - * @brief Wait for events on a single GPIO line. - * @param device Name, path or number of the gpiochip. - * @param offset GPIO line offset on the chip. - * @param active_low The active state of this line - true if low. - * @param timeout Maximum wait time for each iteration. - * @param callback Callback function to call on event occurence. - * @param cbdata User data passed to the callback. - * @return 0 no errors were encountered, -1 if an error occured. - * - */ -int gpiod_simple_event_loop(const char *device, unsigned int offset, - bool active_low, struct timespec *timeout, - gpiod_event_cb callback, void *cbdata) GPIOD_API; - -/** - * @} - * - * @defgroup __lines__ GPIO line operations - * @{ - * - * Functions and data structures dealing with GPIO lines. - */ - -/** - * @brief Available direction settings. - * - * These values are used both when requesting lines and when retrieving - * line info. - */ -enum { - GPIOD_DIRECTION_AS_IS, - /**< Only relevant for line requests - don't set the direction. */ - GPIOD_DIRECTION_INPUT, - /**< Direction is input - we're reading the state of a GPIO line. */ - GPIOD_DIRECTION_OUTPUT, - /**< Direction is output - we're driving the GPIO line. */ -}; - -/** - * @brief Available active state settings. - */ -enum { - GPIOD_ACTIVE_STATE_HIGH, - /**< The active state of a GPIO is active-high. */ - GPIOD_ACTIVE_STATE_LOW, - /**< The active state of a GPIO is active-low. */ -}; - -/** - * @brief Miscellaneous GPIO flags. - */ -enum { - GPIOD_REQUEST_OPEN_DRAIN = GPIOD_BIT(0), - /**< The line is an open-drain port. */ - GPIOD_REQUEST_OPEN_SOURCE = GPIOD_BIT(1), - /**< The line is an open-source port. */ -}; - -/** - * @brief Maximum number of GPIO lines that can be requested at once. - */ -#define GPIOD_REQUEST_MAX_LINES 64 - -/** - * @brief Helper structure for storing a set of GPIO line objects. - * - * This structure is used in all operations involving sets of GPIO lines. - */ -struct gpiod_line_bulk { - struct gpiod_line *lines[GPIOD_REQUEST_MAX_LINES]; - /**< Buffer for line pointers. */ - unsigned int num_lines; - /**< Number of lines currently held in this structure. */ -}; - -/** - * @brief Static initializer for GPIO bulk objects. - * - * This macro simply sets the internally held number of lines to 0. - */ -#define GPIOD_LINE_BULK_INITIALIZER { .num_lines = 0, } - -/** - * @brief Initialize a GPIO bulk object. - * @param bulk Line bulk object. - * - * This routine simply sets the internally held number of lines to 0. - */ -static inline void gpiod_line_bulk_init(struct gpiod_line_bulk *bulk) -{ - bulk->num_lines = 0; -} - -/** - * @brief Add a single line to a GPIO bulk object. - * @param bulk Line bulk object. - * @param line Line to add. - */ -static inline void gpiod_line_bulk_add(struct gpiod_line_bulk *bulk, - struct gpiod_line *line) -{ - bulk->lines[bulk->num_lines++] = line; -} - -/** - * @brief Read the GPIO line offset. - * @param line GPIO line object. - * @return Line offset. - */ -unsigned int gpiod_line_offset(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Read the GPIO line name. - * @param line GPIO line object. - * @return Name of the GPIO line as it is represented in the kernel. This - * routine returns a pointer to a null-terminated string or NULL if - * the line is unnamed. - */ -const char * gpiod_line_name(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Read the GPIO line consumer name. - * @param line GPIO line object. - * @return Name of the GPIO consumer name as it is represented in the - * kernel. This routine returns a pointer to a null-terminated string - * or NULL if the line is not used. - */ -const char * gpiod_line_consumer(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Read the GPIO line direction setting. - * @param line GPIO line object. - * @return Returns GPIOD_DIRECTION_INPUT or GPIOD_DIRECTION_OUTPUT. - */ -int gpiod_line_direction(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Read the GPIO line active state setting. - * @param line GPIO line object. - * @return Returns GPIOD_ACTIVE_STATE_HIGH or GPIOD_ACTIVE_STATE_LOW. - */ -int gpiod_line_active_state(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Check if the line is used by the kernel. - * @param line GPIO line object. - * @return True if the line is used by the kernel, false otherwise. - */ -bool gpiod_line_is_used_by_kernel(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Check if the line is an open-drain GPIO. - * @param line GPIO line object. - * @return True if the line is an open-drain GPIO, false otherwise. - */ -bool gpiod_line_is_open_drain(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Check if the line is an open-source GPIO. - * @param line GPIO line object. - * @return True if the line is an open-source GPIO, false otherwise. - */ -bool gpiod_line_is_open_source(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Re-read the line info. - * @param line GPIO line object. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - * - * The line info is initially retrieved from the kernel by - * gpiod_chip_get_line(). Users can use this line to manually re-read the line - * info. - */ -int gpiod_line_update(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Check if the line info needs to be updated. - * @param line GPIO line object. - * @return Returns false if the line is up-to-date. True otherwise. - * - * The line is updated by calling gpiod_line_update() from within - * gpiod_chip_get_line() and on every line request/release. However: an error - * returned from gpiod_line_update() only breaks the execution of the former. - * The request/release routines only set the internal up-to-date flag to false - * and continue their execution. This routine allows to check if a line info - * update failed at some point and we should call gpiod_line_update() - * explicitly. - */ -bool gpiod_line_needs_update(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Structure holding configuration of a line request. - */ -struct gpiod_line_request_config { - const char *consumer; - /**< Name of the consumer. */ - int direction; - /**< Requested direction. */ - int active_state; - /**< Requested active state configuration. */ - int flags; - /**< Other configuration flags. */ -}; - -/** - * @brief Reserve a single line. - * @param line GPIO line object. - * @param config Request options. - * @param default_val Default line value - only relevant if we're setting - * the direction to output. - * @return 0 if the line was properly reserved. In case of an error this - * routine returns -1 and sets the last error number. - * - * Is this routine succeeds, the caller takes ownership of the GPIO line until - * it's released. - */ -int gpiod_line_request(struct gpiod_line *line, - const struct gpiod_line_request_config *config, - int default_val) GPIOD_API; - -/** - * @brief Reserve a single line, set the direction to input. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @param active_low Active state of the line (true if low). - * @return 0 if the line was properly reserved, -1 on failure. - */ -static inline int gpiod_line_request_input(struct gpiod_line *line, - const char *consumer, - bool active_low) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .direction = GPIOD_DIRECTION_INPUT, - .active_state = active_low ? GPIOD_ACTIVE_STATE_LOW - : GPIOD_ACTIVE_STATE_HIGH, - }; - - return gpiod_line_request(line, &config, 0); -} - -/** - * @brief Reserve a single line, set the direction to output. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @param active_low Active state of the line (true if low). - * @param default_val Default line value. - * @return 0 if the line was properly reserved, -1 on failure. - */ -static inline int gpiod_line_request_output(struct gpiod_line *line, - const char *consumer, - bool active_low, int default_val) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .direction = GPIOD_DIRECTION_OUTPUT, - .active_state = active_low ? GPIOD_ACTIVE_STATE_LOW - : GPIOD_ACTIVE_STATE_HIGH, - }; - - return gpiod_line_request(line, &config, default_val); -} - -/** - * @brief Reserve a set of GPIO lines. - * @param bulk Set of GPIO lines to reserve. - * @param config Request options. - * @param default_vals Default line values - only relevant if we're setting - * the direction to output. - * @return 0 if the all lines were properly requested. In case of an error - * this routine returns -1 and sets the last error number. - * - * Is this routine succeeds, the caller takes ownership of the GPIO line until - * it's released. - */ -int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk, - const struct gpiod_line_request_config *config, - int *default_vals) GPIOD_API; - -/** - * @brief Reserve a set of GPIO lines, set the direction to input. - * @param bulk Set of GPIO lines to reserve. - * @param consumer Name of the consumer. - * @param active_low Active state of the lines (true if low). - * @return 0 if the lines were properly reserved, -1 on failure. - */ -static inline int gpiod_line_request_bulk_input(struct gpiod_line_bulk *bulk, - const char *consumer, - bool active_low) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .direction = GPIOD_DIRECTION_INPUT, - .active_state = active_low ? GPIOD_ACTIVE_STATE_LOW - : GPIOD_ACTIVE_STATE_HIGH, - }; - - return gpiod_line_request_bulk(bulk, &config, 0); -} - -/** - * @brief Reserve a set of GPIO lines, set the direction to output. - * @param bulk Set of GPIO lines to reserve. - * @param consumer Name of the consumer. - * @param active_low Active state of the lines (true if low). - * @param default_vals Default line values. - * @return 0 if the lines were properly reserved, -1 on failure. - */ -static inline int gpiod_line_request_bulk_output(struct gpiod_line_bulk *bulk, - const char *consumer, - bool active_low, - int *default_vals) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .direction = GPIOD_DIRECTION_OUTPUT, - .active_state = active_low ? GPIOD_ACTIVE_STATE_LOW - : GPIOD_ACTIVE_STATE_HIGH, - }; - - return gpiod_line_request_bulk(bulk, &config, default_vals); -} - -/** - * @brief Release a previously reserved line. - * @param line GPIO line object. - */ -void gpiod_line_release(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Release a set of previously reserved lines. - * @param bulk Set of GPIO lines to release. - */ -void gpiod_line_release_bulk(struct gpiod_line_bulk *bulk) GPIOD_API; - -/** - * @brief Check if the calling user has ownership of this line. - * @param line GPIO line object. - * @return True if given line was requested, false otherwise. - */ -bool gpiod_line_is_reserved(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Check if the calling user has neither requested ownership of this - * line nor configured any event notifications. - * @param line GPIO line object. - * @return True if given line is free, false otherwise. - */ -bool gpiod_line_is_free(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Read current value of a single GPIO line. - * @param line GPIO line object. - * @return 0 or 1 if the operation succeeds. On error this routine returns -1 - * and sets the last error number. - */ -int gpiod_line_get_value(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Read current values of a set of GPIO lines. - * @param bulk Set of GPIO lines to reserve. - * @param values An array big enough to hold line_bulk->num_lines values. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - * - * If succeeds, this routine fills the values array with a set of values in - * the same order, the lines are added to line_bulk. - */ -int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, - int *values) GPIOD_API; - -/** - * @brief Set the value of a single GPIO line. - * @param line GPIO line object. - * @param value New value. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - */ -int gpiod_line_set_value(struct gpiod_line *line, int value) GPIOD_API; - -/** - * @brief Set the values of a set of GPIO lines. - * @param bulk Set of GPIO lines to reserve. - * @param values An array holding line_bulk->num_lines new values for lines. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - */ -int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, - int *values) GPIOD_API; - -/** - * @brief Find a GPIO line by its name. - * @param name Name of the GPIO line. - * @return Returns the GPIO line handle if the line exists in the system or - * NULL if it couldn't be located. - * - * If this routine succeeds, the user must manually close the GPIO chip owning - * this line to avoid memory leaks. - */ -struct gpiod_line * gpiod_line_find_by_name(const char *name) GPIOD_API; - -/** - * @brief Get the handle to the GPIO chip controlling this line. - * @param line The GPIO line object. - * @return Pointer to the GPIO chip handle controlling this line. - */ -struct gpiod_chip * gpiod_line_get_chip(struct gpiod_line *line) GPIOD_API; - -/** - * @defgroup __line_events__ Line event operations - * @{ - * - * Functions and data structures for requesting and reading GPIO line events. - */ - -/** - * @brief Event types. - */ -enum { - GPIOD_EVENT_RISING_EDGE, - /**< Rising edge event. */ - GPIOD_EVENT_FALLING_EDGE, - /**< Falling edge event. */ - GPIOD_EVENT_BOTH_EDGES, - /**< Rising or falling edge event: only relevant for event requests. */ -}; - -/** - * @brief Structure holding configuration of a line event request. - */ -struct gpiod_line_evreq_config { - const char *consumer; - /**< Name of the consumer. */ - int event_type; - /**< Type of the event we want to be notified about. */ - int active_state; - /**< GPIO line active state. */ - int line_flags; - /**< Misc line flags - same as for line requests. */ -}; - -/** - * @brief Structure holding event info. - */ -struct gpiod_line_event { - struct timespec ts; - /**< Best estimate of time of event occurrence. */ - int event_type; - /**< Type of the event that occurred. */ -}; - -/** - * @brief Request event notifications for a single line. - * @param line GPIO line object. - * @param config Event request configuration. - * @return 0 if the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - */ -int gpiod_line_event_request(struct gpiod_line *line, - struct gpiod_line_evreq_config *config) GPIOD_API; - -/** - * @brief Request all event type notifications on a single line. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @param active_low Active state of the line - true if low. - * @return 0 if the operation succeeds, -1 on failure. - */ -static inline int gpiod_line_event_request_all(struct gpiod_line *line, - const char *consumer, - bool active_low) -{ - struct gpiod_line_evreq_config config = { - .consumer = consumer, - .event_type = GPIOD_EVENT_BOTH_EDGES, - .active_state = active_low ? GPIOD_ACTIVE_STATE_LOW - : GPIOD_ACTIVE_STATE_HIGH, - }; - - return gpiod_line_event_request(line, &config); -} - -/** - * @brief Stop listening for events and release the line. - * @param line GPIO line object. - */ -void gpiod_line_event_release(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Check if event notifications are configured on this line. - * @param line GPIO line object. - * @return True if event notifications are configured. False otherwise. - */ -bool gpiod_line_event_configured(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Wait for an event on a single line. - * @param line GPIO line object. - * @param timeout Wait time limit. - * @return 0 if wait timed out, -1 if an error occurred, 1 if an event - * occurred. - */ -int gpiod_line_event_wait(struct gpiod_line *line, - const struct timespec *timeout) GPIOD_API; - -/** - * @brief Wait for the first event on a set of lines. - * @param bulk Set of GPIO lines to monitor. - * @param timeout Wait time limit. - * @param line The handle of the line on which an event occurs is stored - * in this variable. Can be NULL. - * @return 0 if wait timed out, -1 if an error occurred, 1 if an event - * occurred. - */ -int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk, - const struct timespec *timeout, - struct gpiod_line **line) GPIOD_API; - -/** - * @brief Read the last event from the GPIO line. - * @param line GPIO line object. - * @param event Buffer to which the event data will be copied. - * @return 0 if the event was read correctly, -1 on error. - */ -int gpiod_line_event_read(struct gpiod_line *line, - struct gpiod_line_event *event) GPIOD_API; - -/** - * @brief Get the event file descriptor. - * @param line GPIO line object. - * @return Number of the event file descriptor or -1 on error. - * - * Users may want to poll the event file descriptor on their own. This routine - * allows to access it. - */ -int gpiod_line_event_get_fd(struct gpiod_line *line) GPIOD_API; - -/** - * @brief Read the last GPIO event directly from a file descriptor. - * @param fd File descriptor. - * @param event Buffer to which the event data will be copied. - * @return 0 if the event was read correctly, -1 on error. - * - * Users who directly poll the file descriptor for incoming events can also - * directly read the event data from it using this routine. This function - * translates the kernel representation of the event to the libgpiod format. - */ -int gpiod_line_event_read_fd(int fd, struct gpiod_line_event *event) GPIOD_API; - -/** - * @} - * - * @} - * - * @defgroup __chips__ GPIO chip operations - * @{ - * - * Functions and data structures dealing with GPIO chips. - */ - -/** - * @brief Open a gpiochip by path. - * @param path Path to the gpiochip device file. - * @return GPIO chip handle or NULL if an error occurred. - */ -struct gpiod_chip * gpiod_chip_open(const char *path) GPIOD_API; - -/** - * @brief Open a gpiochip by name. - * @param name Name of the gpiochip to open. - * @return GPIO chip handle or NULL if an error occurred. - * - * This routine appends name to '/dev/' to create the path. - */ -struct gpiod_chip * gpiod_chip_open_by_name(const char *name) GPIOD_API; - -/** - * @brief Open a gpiochip by number. - * @param num Number of the gpiochip. - * @return GPIO chip handle or NULL if an error occurred. - * - * This routine appends num to '/dev/gpiochip' to create the path. - */ -struct gpiod_chip * gpiod_chip_open_by_number(unsigned int num) GPIOD_API; - -/** - * @brief Open a gpiochip based on the best guess what the path is. - * @param descr String describing the gpiochip. - * @return GPIO chip handle or NULL if an error occurred. - * - * This routine tries to figure out whether the user passed it the path to - * the GPIO chip, its name or number as a string. Then it tries to open it - * using one of the other gpiod_chip_open** routines. - */ -struct gpiod_chip * gpiod_chip_open_lookup(const char *descr) GPIOD_API; - -/** - * @brief Close a GPIO chip handle and release all allocated resources. - * @param chip The GPIO chip object. - */ -void gpiod_chip_close(struct gpiod_chip *chip) GPIOD_API; - -/** - * @brief Get the GPIO chip name as represented in the kernel. - * @param chip The GPIO chip object. - * @return Pointer to a human-readable string containing the chip name. - */ -const char * gpiod_chip_name(struct gpiod_chip *chip) GPIOD_API; - -/** - * @brief Get the GPIO chip label as represented in the kernel. - * @param chip The GPIO chip object. - * @return Pointer to a human-readable string containing the chip label. - */ -const char * gpiod_chip_label(struct gpiod_chip *chip) GPIOD_API; - -/** - * @brief Get the number of GPIO lines exposed by this chip. - * @param chip The GPIO chip object. - * @return Number of GPIO lines. - */ -unsigned int gpiod_chip_num_lines(struct gpiod_chip *chip) GPIOD_API; - -/** - * @brief Get the handle to the GPIO line at given offset. - * @param chip The GPIO chip object. - * @param offset The offset of the GPIO line. - * @return Pointer to the GPIO line handle or NULL if an error occured. - */ -struct gpiod_line * -gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset) GPIOD_API; - -/** - * @} - * - * @defgroup __iterators__ Iterators for GPIO chips and lines - * @{ - * - * These functions and data structures allow easy iterating over GPIO - * chips and lines. - */ - -/** - * @brief Create a new gpiochip iterator. - * @return Pointer to a new chip iterator object or NULL if an error occurred. - */ -struct gpiod_chip_iter * gpiod_chip_iter_new(void) GPIOD_API; - -/** - * @brief Release all resources allocated for the gpiochip iterator and close - * the most recently opened gpiochip (if any). - * @param iter The gpiochip iterator object. - */ -void gpiod_chip_iter_free(struct gpiod_chip_iter *iter) GPIOD_API; - -/** - * @brief Release all resources allocated for the gpiochip iterator but - * don't close the most recently opened gpiochip (if any). - * @param iter The gpiochip iterator object. - * - * Users may want to break the loop when iterating over gpiochips and keep - * the most recently opened chip active while freeing the iterator data. - * This routine enables that. - */ -void gpiod_chip_iter_free_noclose(struct gpiod_chip_iter *iter) GPIOD_API; - -/** - * @brief Get the next gpiochip handle. - * @param iter The gpiochip iterator object. - * @return Pointer to an open gpiochip handle or NULL if the next chip can't - * be accessed. - * - * Internally this routine scans the /dev/ directory, storing current state - * in the chip iterator structure, and tries to open the next /dev/gpiochipX - * device file. If an error occurs or no more chips are present, the function - * returns NULL. - */ -struct gpiod_chip * -gpiod_chip_iter_next(struct gpiod_chip_iter *iter) GPIOD_API; - -/** - * @brief Iterate over all gpiochip present in the system. - * @param iter An initialized GPIO chip iterator. - * @param chip Pointer to a GPIO chip handle. On each iteration the newly - * opened chip handle is assigned to this argument. - * - * The user must not close the GPIO chip manually - instead the previous chip - * handle is closed automatically on the next iteration. The last chip to be - * opened is closed internally by gpiod_chip_iter_free(). - */ -#define gpiod_foreach_chip(iter, chip) \ - for ((chip) = gpiod_chip_iter_next(iter); \ - !gpiod_chip_iter_done(iter); \ - (chip) = gpiod_chip_iter_next(iter)) - -/** - * @brief Check if we're done iterating over gpiochips on this iterator. - * @param iter The gpiochip iterator object. - * @return True if we've iterated over all chips, false otherwise. - */ -bool gpiod_chip_iter_done(struct gpiod_chip_iter *iter) GPIOD_API; - -/** - * @brief Check if we've encountered an error condition while opening a - * gpiochip. - * @param iter The gpiochip iterator object. - * @return True if there was an error opening a gpiochip device file, - * false otherwise. - */ -bool gpiod_chip_iter_iserr(struct gpiod_chip_iter *iter) GPIOD_API; - -/** - * @brief Get the name of the gpiochip that we failed to access. - * @param iter The gpiochip iterator object. - * @return If gpiod_chip_iter_iserr() returned true, this function returns a - * pointer to the name of the gpiochip that we failed to access. - * If there was no error this function returns NULL. - * - * NOTE: this function will return NULL if the internal memory allocation - * fails. - */ -const char * -gpiod_chip_iter_failed_chip(struct gpiod_chip_iter *iter) GPIOD_API; - -/** - * @brief Possible states of a line iterator. - */ -enum { - GPIOD_LINE_ITER_INIT = 0, - /**< Line iterator is initiated or iterating over lines. */ - GPIOD_LINE_ITER_DONE, - /**< Line iterator is done with all lines on this chip. */ - GPIOD_LINE_ITER_ERR, - /**< There was an error retrieving info for a line. */ -}; - -/** - * @brief GPIO line iterator structure. - * - * This structure is used in conjunction with gpiod_line_iter_next() to - * iterate over all GPIO lines of a single GPIO chip. - */ -struct gpiod_line_iter { - unsigned int offset; - /**< Current line offset. */ - struct gpiod_chip *chip; - /**< GPIO chip whose line we're iterating over. */ - int state; - /**< Current state of the iterator. */ -}; - -/** - * @brief Static initializer for line iterators. - * @param chip The gpiochip object whose lines we want to iterate over. - */ -#define GPIOD_LINE_ITER_INITIALIZER(chip) \ - { \ - .offset = 0, \ - .chip = (chip), \ - .state = GPIOD_LINE_ITER_INIT, \ - } - -/** - * @brief Initialize a GPIO line iterator. - * @param iter Pointer to a GPIO line iterator. - * @param chip The gpiochip object whose lines we want to iterate over. - */ -static inline void gpiod_line_iter_init(struct gpiod_line_iter *iter, - struct gpiod_chip *chip) -{ - iter->offset = 0; - iter->chip = chip; - iter->state = GPIOD_LINE_ITER_INIT; -} - -/** - * @brief Get the next GPIO line handle. - * @param iter The GPIO line iterator object. - * @return Pointer to the next GPIO line handle or NULL if no more lines or - * and error occured. - */ -struct gpiod_line * -gpiod_line_iter_next(struct gpiod_line_iter *iter) GPIOD_API; - -/** - * @brief Check if we're done iterating over lines on this iterator. - * @param iter The GPIO line iterator object. - * @return True if we've iterated over all lines, false otherwise. - */ -static inline bool gpiod_line_iter_done(const struct gpiod_line_iter *iter) -{ - return iter->state == GPIOD_LINE_ITER_DONE; -} - -/** - * @brief Check if we've encountered an error condition while retrieving - * info for a line. - * @param iter The GPIO line iterator object. - * @return True if there was an error retrieving info about a GPIO line, - * false otherwise. - */ -static inline bool gpiod_line_iter_err(const struct gpiod_line_iter *iter) -{ - return iter->state == GPIOD_LINE_ITER_ERR; -} - -/** - * @brief Get the offset of the last line we tried to open. - * @param iter The GPIO line iterator object. - * @return The offset of the last line we tried to open - whether we failed - * or succeeded to do so. - */ -static inline unsigned int -gpiod_line_iter_last_offset(const struct gpiod_line_iter *iter) -{ - return iter->offset - 1; -} - -/** - * @brief Iterate over all GPIO lines of a single chip. - * @param iter An initialized GPIO line iterator. - * @param line Pointer to a GPIO line handle - on each iteration, the - * next GPIO line will be assigned to this argument. - */ -#define gpiod_foreach_line(iter, line) \ - for ((line) = gpiod_line_iter_next(iter); \ - !gpiod_line_iter_done(iter); \ - (line) = gpiod_line_iter_next(iter)) - -/** - * @} - * - * @defgroup __misc__ Stuff that didn't fit anywhere else - * @{ - * - * Various libgpiod-related functions. - */ - -/** - * @brief Get the version of the library as a human-readable string. - * @return Human-readable string containing the library version. - */ -const char * gpiod_version_string(void) GPIOD_API; - -/** - * @} - */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* __LIBGPIOD_GPIOD_H__ */ diff --git a/gpiodetect.c b/gpiodetect.c deleted file mode 100644 index 507605a..0000000 --- a/gpiodetect.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * List all GPIO chips. - * - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - */ - -#include "gpiod.h" -#include "tools-common.h" - -#include -#include -#include - -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { 0 }, -}; - -static const char *const shortopts = "+hv"; - -static void print_help(void) -{ - printf("Usage: %s \n", get_progname()); - printf("List all GPIO chips\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; - - set_progname(argv[0]); - - 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) { - if (gpiod_chip_iter_iserr(iter)) - die_perror("error accessing gpiochip %s", - gpiod_chip_iter_failed_chip(iter)); - - 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; -} diff --git a/gpiofind.c b/gpiofind.c deleted file mode 100644 index f390672..0000000 --- a/gpiofind.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Read value from GPIO line. - * - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - */ - -#include "gpiod.h" -#include "tools-common.h" - -#include -#include -#include - -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { 0 }, -}; - -static const char *const shortopts = "+hv"; - -static void print_help(void) -{ - printf("Usage: %s [NAME]\n", get_progname()); - printf("Find a GPIO line by name.\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_line *line; - struct gpiod_chip *chip; - int optc, opti; - - set_progname(argv[0]); - - 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("GPIO line name must be specified"); - - line = gpiod_line_find_by_name(argv[0]); - if (!line) - return EXIT_FAILURE; - - chip = gpiod_line_get_chip(line); - - printf("%s %u\n", gpiod_chip_name(chip), gpiod_line_offset(line)); - - gpiod_chip_close(chip); - - return EXIT_SUCCESS; -} diff --git a/gpioget.c b/gpioget.c deleted file mode 100644 index 41ccb9c..0000000 --- a/gpioget.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Read value from GPIO line. - * - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - */ - -#include "gpiod.h" -#include "tools-common.h" - -#include -#include -#include - -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { "active-low", no_argument, NULL, 'l' }, - { 0 }, -}; - -static const char *const shortopts = "hvl"; - -static void print_help(void) -{ - printf("Usage: %s [CHIP NAME/NUMBER] [LINE OFFSET] \n", - get_progname()); - printf("Read value from a GPIO line\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) -{ - bool active_low = false; - int value, optc, opti; - unsigned int offset; - char *device, *end; - - set_progname(argv[0]); - - 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("gpio line offset must be specified"); - - device = argv[0]; - - offset = strtoul(argv[1], &end, 10); - if (*end != '\0') - die("invalid GPIO offset: %s", argv[1]); - - value = gpiod_simple_get_value(device, offset, active_low); - if (value < 0) - die_perror("error reading GPIO value"); - - printf("%d\n", value); - - return EXIT_SUCCESS; -} diff --git a/gpioinfo.c b/gpioinfo.c deleted file mode 100644 index 8609b77..0000000 --- a/gpioinfo.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * List all lines of a GPIO chip. - * - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - */ - -#include "gpiod.h" -#include "tools-common.h" - -#include -#include -#include -#include -#include - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) - -struct flag { - const char *name; - bool (*is_set)(struct gpiod_line *); -}; - -static const struct flag flags[] = { - { - .name = "kernel", - .is_set = gpiod_line_is_used_by_kernel, - }, - { - .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' }, - { 0 }, -}; - -static const char *const shortopts = "+hv"; - -static void print_help(void) -{ - printf("Usage: %s [GPIOCHIP1...]\n", get_progname()); - printf("List all lines of a GPIO chip.\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 *overflow, - unsigned int pref_len, const char *fmt, ...) -{ - char *buf, *buffmt = NULL; - size_t len; - va_list va; - int status; - - va_start(va, fmt); - status = vasprintf(&buf, fmt, va); - va_end(va); - if (status < 0) - die("vasprintf: %s\n", strerror(errno)); - - len = strlen(buf) - 1; - - if (len >= pref_len || *overflow) { - *overflow = true; - printf("%s", buf); - } else { - status = asprintf(&buffmt, "%%%us", pref_len); - if (status < 0) - die("asprintf: %s\n", strerror(errno)); - - printf(buffmt, buf); - } - - free(buf); - if (fmt) - free(buffmt); -} - -static void list_lines(struct gpiod_chip *chip) -{ - bool flag_printed, overflow; - int direction, active_state; - struct gpiod_line_iter iter; - const char *name, *consumer; - struct gpiod_line *line; - unsigned int i, offset; - - printf("%s - %u lines:\n", - gpiod_chip_name(chip), gpiod_chip_num_lines(chip)); - - gpiod_line_iter_init(&iter, chip); - gpiod_foreach_line(&iter, line) { - if (gpiod_line_iter_err(&iter)) - die_perror("error retrieving info for line %u", - gpiod_line_iter_last_offset(&iter)); - - 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); - - overflow = false; - - printf("\tline "); - prinfo(&overflow, 2, "%u", offset); - printf(": "); - - name ? prinfo(&overflow, 12, "\"%s\"", name) - : prinfo(&overflow, 12, "unnamed"); - printf(" "); - - consumer ? prinfo(&overflow, 12, "\"%s\"", consumer) - : prinfo(&overflow, 12, "unused"); - printf(" "); - - prinfo(&overflow, 8, "%s ", direction == GPIOD_DIRECTION_INPUT - ? "input" : "output"); - prinfo(&overflow, 13, "%s ", - active_state == GPIOD_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"); - } -} - -int main(int argc, char **argv) -{ - struct gpiod_chip_iter *chip_iter; - struct gpiod_chip *chip; - int i, optc, opti; - - set_progname(argv[0]); - - 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) { - if (gpiod_chip_iter_iserr(chip_iter)) - die_perror("error accessing gpiochip %s", - gpiod_chip_iter_failed_chip(chip_iter)); - - 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; -} diff --git a/gpiomon.c b/gpiomon.c deleted file mode 100644 index 54da482..0000000 --- a/gpiomon.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Monitor events on a GPIO line. - * - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - */ - -#include "gpiod.h" -#include "tools-common.h" - -#include -#include -#include -#include - -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { "active-low", no_argument, NULL, 'l' }, - { 0 }, -}; - -static const char *const shortopts = "hvl"; - -static void print_help(void) -{ - printf("Usage: %s [CHIP NAME/NUMBER] [LINE OFFSET] \n", - get_progname()); - printf("Wait for events on a GPIO line\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"); -} - -static volatile bool do_run = true; - -static void sighandler(int signum UNUSED) -{ - do_run = false; -} - -static int event_callback(int type, const struct timespec *ts, - void *data UNUSED) -{ - const char *evname = NULL; - - switch (type) { - case GPIOD_EVENT_CB_RISING_EDGE: - evname = " RISING EDGE"; - break; - case GPIOD_EVENT_CB_FALLING_EDGE: - evname = "FALLING_EDGE"; - break; - default: - break; - } - - if (evname) - printf("GPIO EVENT: %s [%8ld.%09ld]\n", - evname, ts->tv_sec, ts->tv_nsec); - - if (!do_run) - return GPIOD_EVENT_CB_STOP; - - return GPIOD_EVENT_CB_OK; -} - -int main(int argc, char **argv) -{ - bool active_low = false; - struct timespec timeout; - int optc, opti, status; - unsigned int offset; - char *device, *end; - - set_progname(argv[0]); - - 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("gpio line offset must be specified"); - - device = argv[0]; - offset = strtoul(argv[1], &end, 10); - if (*end != '\0') - die("invalid GPIO offset: %s", argv[1]); - - timeout.tv_sec = 0; - timeout.tv_nsec = 500000000; - - signal(SIGINT, sighandler); - signal(SIGTERM, sighandler); - - status = gpiod_simple_event_loop(device, offset, active_low, - &timeout, event_callback, NULL); - if (status < 0) - die_perror("error waiting for events"); - - return EXIT_SUCCESS; -} diff --git a/gpioset.c b/gpioset.c deleted file mode 100644 index 65dd70e..0000000 --- a/gpioset.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Set value of a GPIO line. - * - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - */ - -#include "gpiod.h" -#include "tools-common.h" - -#include -#include -#include - -static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { "active-low", no_argument, NULL, 'l' }, - { 0 }, -}; - -static const char *const shortopts = "hvl"; - -static void print_help(void) -{ - printf("Usage: %s [CHIP NAME/NUMBER] [LINE OFFSET] [VALUE] \n", - get_progname()); - printf("Set value of a GPIO line\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"); - printf("This program reserves the GPIO line, sets its value and waits for the user to press ENTER before releasing the line\n"); -} - -static void wait_for_enter(void *data UNUSED) -{ - getchar(); -} - -int main(int argc, char **argv) -{ - int value, status, optc, opti; - bool active_low = false; - unsigned int offset; - char *device, *end; - - set_progname(argv[0]); - - 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("gpio line offset must be specified"); - - if (argc < 3) - die("value must be specified"); - - device = argv[0]; - - offset = strtoul(argv[1], &end, 10); - if (*end != '\0') - die("invalid GPIO offset: %s", argv[1]); - - value = strtoul(argv[2], &end, 10); - if (*end != '\0' || (value != 0 && value != 1)) - die("invalid value: %s", argv[2]); - - status = gpiod_simple_set_value(device, offset, value, - active_low, wait_for_enter, NULL); - if (status < 0) - die_perror("error setting the GPIO line value"); - - return EXIT_SUCCESS; -} diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..74566a8 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,9 @@ +# +# Copyright (C) 2017 Bartosz Golaszewski +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# + +include_HEADERS = gpiod.h diff --git a/include/gpiod.h b/include/gpiod.h new file mode 100644 index 0000000..f6cfefd --- /dev/null +++ b/include/gpiod.h @@ -0,0 +1,1029 @@ +/* + * GPIO chardev utils for linux. + * + * Copyright (C) 2017 Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +/** + * @mainpage libgpiod public API + * + * This is the documentation of the public API exported by libgpiod. + * + *

These functions and data structures allow to use all the functionalities + * exposed by the linux GPIO character device interface. + */ + +#ifndef __LIBGPIOD_GPIOD_H__ +#define __LIBGPIOD_GPIOD_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct gpiod_chip; +struct gpiod_line; +struct gpiod_chip_iter; + +/** + * @defgroup __common__ Common helper macros + * @{ + * + * Commonly used utility macros. + */ + +/** + * @brief Makes symbol visible. + */ +#define GPIOD_API __attribute__((visibility("default"))) + +/** + * @brief Shift 1 by given offset. + * @param nr Bit position. + * @return 1 shifted by nr. + */ +#define GPIOD_BIT(nr) (1UL << (nr)) + +/** + * @} + * + * @defgroup __error__ Error handling + * @{ + * + * Error handling functions and macros. This library uses a combination of + * system-wide errno numbers and internal GPIO-specific errors. The routines + * in this section should be used to access the information about any error + * conditions that may occur. + * + * With some noted exceptions, all libgpiod functions set the last error + * variable if an error occurs. Internally, the last_error variable has a + * separate instance per thread making the library thread-safe. + */ + +/** + * @brief Private: offset for all libgpiod error numbers. + */ +#define __GPIOD_ERRNO_OFFSET 10000 + +/** + * @brief libgpiod-specific error numbers. + */ +enum { + GPIOD_ESUCCESS = __GPIOD_ERRNO_OFFSET, + /**< No error. */ + GPIOD_EREQUEST, + /**< The caller has no ownership of this line. */ + GPIOD_EEVREQUEST, + /**< The caller has not configured any events on this line. */ + GPIOD_EBULKINCOH, + /**< Not all lines in bulk belong to the same GPIO chip. */ + GPIOD_ELINEBUSY, + /**< This line is currently in use. */ + __GPIOD_MAX_ERR, + /**< Private: number of libgpiod-specific error numbers. */ +}; + +/** + * @brief Return last error. + * @return Number of the last error inside libgpiod. + */ +int gpiod_errno(void) GPIOD_API; + +/** + * @brief Convert error number to a human-readable string. + * @param errnum Error number to convert. + * @return Pointer to a null-terminated error description. + */ +const char * gpiod_strerror(int errnum) GPIOD_API; + +/** + * @brief Convert the last libgpiod error number to a human-readable string. + * @return Pointer to a null-terminated error description. + */ +const char * gpiod_last_strerror(void) GPIOD_API; + +/** + * @} + * + * @defgroup __high_level__ High-level API + * @{ + * + * Simple high-level routines for straightforward GPIO manipulation. + */ + +/** + * @brief Read current value from a single GPIO line. + * @param device Name, path or number of the gpiochip. + * @param offset GPIO line offset on the chip. + * @param active_low The active state of this line - true if low. + * @return 0 or 1 if the operation succeeds. On error this routine returns -1 + * and sets the last error number. + */ +int gpiod_simple_get_value(const char *device, + unsigned int offset, bool active_low) GPIOD_API; + +/** + * @brief Set value of a single GPIO line. + * @param device Name, path or number of the gpiochip. + * @param offset GPIO line offset on the chip. + * @param value New value. + * @param active_low The active state of this line - true if low. + * @param cb Callback function that will be called right after the value is + * set. Users can use this, for example, to pause the execution after + * toggling a GPIO. + * @param data User data that will be passed to the callback function. + * @return 0 or 1 if the operation succeeds. On error this routine returns -1 + * and sets the last error number. + */ +int gpiod_simple_set_value(const char *device, unsigned int offset, + int value, bool active_low, void (*cb)(void *), + void *data) GPIOD_API; + +/** + * @brief Event types that can be passed to the simple event callback. + */ +enum { + GPIOD_EVENT_CB_TIMEOUT, + /**< Waiting for events timed out. */ + GPIOD_EVENT_CB_RISING_EDGE, + /**< Rising edge event occured. */ + GPIOD_EVENT_CB_FALLING_EDGE, + /**< Falling edge event occured. */ +}; + +/** + * @brief Return status values that the simple event callback can return. + */ +enum { + GPIOD_EVENT_CB_OK = 0, + /**< Continue processing events. */ + GPIOD_EVENT_CB_STOP, + /**< Stop processing events. */ +}; + +/** + * @brief Simple event callack signature. + */ +typedef int (*gpiod_event_cb)(int, const struct timespec *, void *); + +/** + * @brief Wait for events on a single GPIO line. + * @param device Name, path or number of the gpiochip. + * @param offset GPIO line offset on the chip. + * @param active_low The active state of this line - true if low. + * @param timeout Maximum wait time for each iteration. + * @param callback Callback function to call on event occurence. + * @param cbdata User data passed to the callback. + * @return 0 no errors were encountered, -1 if an error occured. + * + */ +int gpiod_simple_event_loop(const char *device, unsigned int offset, + bool active_low, struct timespec *timeout, + gpiod_event_cb callback, void *cbdata) GPIOD_API; + +/** + * @} + * + * @defgroup __lines__ GPIO line operations + * @{ + * + * Functions and data structures dealing with GPIO lines. + */ + +/** + * @brief Available direction settings. + * + * These values are used both when requesting lines and when retrieving + * line info. + */ +enum { + GPIOD_DIRECTION_AS_IS, + /**< Only relevant for line requests - don't set the direction. */ + GPIOD_DIRECTION_INPUT, + /**< Direction is input - we're reading the state of a GPIO line. */ + GPIOD_DIRECTION_OUTPUT, + /**< Direction is output - we're driving the GPIO line. */ +}; + +/** + * @brief Available active state settings. + */ +enum { + GPIOD_ACTIVE_STATE_HIGH, + /**< The active state of a GPIO is active-high. */ + GPIOD_ACTIVE_STATE_LOW, + /**< The active state of a GPIO is active-low. */ +}; + +/** + * @brief Miscellaneous GPIO flags. + */ +enum { + GPIOD_REQUEST_OPEN_DRAIN = GPIOD_BIT(0), + /**< The line is an open-drain port. */ + GPIOD_REQUEST_OPEN_SOURCE = GPIOD_BIT(1), + /**< The line is an open-source port. */ +}; + +/** + * @brief Maximum number of GPIO lines that can be requested at once. + */ +#define GPIOD_REQUEST_MAX_LINES 64 + +/** + * @brief Helper structure for storing a set of GPIO line objects. + * + * This structure is used in all operations involving sets of GPIO lines. + */ +struct gpiod_line_bulk { + struct gpiod_line *lines[GPIOD_REQUEST_MAX_LINES]; + /**< Buffer for line pointers. */ + unsigned int num_lines; + /**< Number of lines currently held in this structure. */ +}; + +/** + * @brief Static initializer for GPIO bulk objects. + * + * This macro simply sets the internally held number of lines to 0. + */ +#define GPIOD_LINE_BULK_INITIALIZER { .num_lines = 0, } + +/** + * @brief Initialize a GPIO bulk object. + * @param bulk Line bulk object. + * + * This routine simply sets the internally held number of lines to 0. + */ +static inline void gpiod_line_bulk_init(struct gpiod_line_bulk *bulk) +{ + bulk->num_lines = 0; +} + +/** + * @brief Add a single line to a GPIO bulk object. + * @param bulk Line bulk object. + * @param line Line to add. + */ +static inline void gpiod_line_bulk_add(struct gpiod_line_bulk *bulk, + struct gpiod_line *line) +{ + bulk->lines[bulk->num_lines++] = line; +} + +/** + * @brief Read the GPIO line offset. + * @param line GPIO line object. + * @return Line offset. + */ +unsigned int gpiod_line_offset(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the GPIO line name. + * @param line GPIO line object. + * @return Name of the GPIO line as it is represented in the kernel. This + * routine returns a pointer to a null-terminated string or NULL if + * the line is unnamed. + */ +const char * gpiod_line_name(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the GPIO line consumer name. + * @param line GPIO line object. + * @return Name of the GPIO consumer name as it is represented in the + * kernel. This routine returns a pointer to a null-terminated string + * or NULL if the line is not used. + */ +const char * gpiod_line_consumer(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the GPIO line direction setting. + * @param line GPIO line object. + * @return Returns GPIOD_DIRECTION_INPUT or GPIOD_DIRECTION_OUTPUT. + */ +int gpiod_line_direction(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the GPIO line active state setting. + * @param line GPIO line object. + * @return Returns GPIOD_ACTIVE_STATE_HIGH or GPIOD_ACTIVE_STATE_LOW. + */ +int gpiod_line_active_state(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if the line is used by the kernel. + * @param line GPIO line object. + * @return True if the line is used by the kernel, false otherwise. + */ +bool gpiod_line_is_used_by_kernel(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if the line is an open-drain GPIO. + * @param line GPIO line object. + * @return True if the line is an open-drain GPIO, false otherwise. + */ +bool gpiod_line_is_open_drain(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if the line is an open-source GPIO. + * @param line GPIO line object. + * @return True if the line is an open-source GPIO, false otherwise. + */ +bool gpiod_line_is_open_source(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Re-read the line info. + * @param line GPIO line object. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + * + * The line info is initially retrieved from the kernel by + * gpiod_chip_get_line(). Users can use this line to manually re-read the line + * info. + */ +int gpiod_line_update(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if the line info needs to be updated. + * @param line GPIO line object. + * @return Returns false if the line is up-to-date. True otherwise. + * + * The line is updated by calling gpiod_line_update() from within + * gpiod_chip_get_line() and on every line request/release. However: an error + * returned from gpiod_line_update() only breaks the execution of the former. + * The request/release routines only set the internal up-to-date flag to false + * and continue their execution. This routine allows to check if a line info + * update failed at some point and we should call gpiod_line_update() + * explicitly. + */ +bool gpiod_line_needs_update(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Structure holding configuration of a line request. + */ +struct gpiod_line_request_config { + const char *consumer; + /**< Name of the consumer. */ + int direction; + /**< Requested direction. */ + int active_state; + /**< Requested active state configuration. */ + int flags; + /**< Other configuration flags. */ +}; + +/** + * @brief Reserve a single line. + * @param line GPIO line object. + * @param config Request options. + * @param default_val Default line value - only relevant if we're setting + * the direction to output. + * @return 0 if the line was properly reserved. In case of an error this + * routine returns -1 and sets the last error number. + * + * Is this routine succeeds, the caller takes ownership of the GPIO line until + * it's released. + */ +int gpiod_line_request(struct gpiod_line *line, + const struct gpiod_line_request_config *config, + int default_val) GPIOD_API; + +/** + * @brief Reserve a single line, set the direction to input. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @param active_low Active state of the line (true if low). + * @return 0 if the line was properly reserved, -1 on failure. + */ +static inline int gpiod_line_request_input(struct gpiod_line *line, + const char *consumer, + bool active_low) +{ + struct gpiod_line_request_config config = { + .consumer = consumer, + .direction = GPIOD_DIRECTION_INPUT, + .active_state = active_low ? GPIOD_ACTIVE_STATE_LOW + : GPIOD_ACTIVE_STATE_HIGH, + }; + + return gpiod_line_request(line, &config, 0); +} + +/** + * @brief Reserve a single line, set the direction to output. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @param active_low Active state of the line (true if low). + * @param default_val Default line value. + * @return 0 if the line was properly reserved, -1 on failure. + */ +static inline int gpiod_line_request_output(struct gpiod_line *line, + const char *consumer, + bool active_low, int default_val) +{ + struct gpiod_line_request_config config = { + .consumer = consumer, + .direction = GPIOD_DIRECTION_OUTPUT, + .active_state = active_low ? GPIOD_ACTIVE_STATE_LOW + : GPIOD_ACTIVE_STATE_HIGH, + }; + + return gpiod_line_request(line, &config, default_val); +} + +/** + * @brief Reserve a set of GPIO lines. + * @param bulk Set of GPIO lines to reserve. + * @param config Request options. + * @param default_vals Default line values - only relevant if we're setting + * the direction to output. + * @return 0 if the all lines were properly requested. In case of an error + * this routine returns -1 and sets the last error number. + * + * Is this routine succeeds, the caller takes ownership of the GPIO line until + * it's released. + */ +int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk, + const struct gpiod_line_request_config *config, + int *default_vals) GPIOD_API; + +/** + * @brief Reserve a set of GPIO lines, set the direction to input. + * @param bulk Set of GPIO lines to reserve. + * @param consumer Name of the consumer. + * @param active_low Active state of the lines (true if low). + * @return 0 if the lines were properly reserved, -1 on failure. + */ +static inline int gpiod_line_request_bulk_input(struct gpiod_line_bulk *bulk, + const char *consumer, + bool active_low) +{ + struct gpiod_line_request_config config = { + .consumer = consumer, + .direction = GPIOD_DIRECTION_INPUT, + .active_state = active_low ? GPIOD_ACTIVE_STATE_LOW + : GPIOD_ACTIVE_STATE_HIGH, + }; + + return gpiod_line_request_bulk(bulk, &config, 0); +} + +/** + * @brief Reserve a set of GPIO lines, set the direction to output. + * @param bulk Set of GPIO lines to reserve. + * @param consumer Name of the consumer. + * @param active_low Active state of the lines (true if low). + * @param default_vals Default line values. + * @return 0 if the lines were properly reserved, -1 on failure. + */ +static inline int gpiod_line_request_bulk_output(struct gpiod_line_bulk *bulk, + const char *consumer, + bool active_low, + int *default_vals) +{ + struct gpiod_line_request_config config = { + .consumer = consumer, + .direction = GPIOD_DIRECTION_OUTPUT, + .active_state = active_low ? GPIOD_ACTIVE_STATE_LOW + : GPIOD_ACTIVE_STATE_HIGH, + }; + + return gpiod_line_request_bulk(bulk, &config, default_vals); +} + +/** + * @brief Release a previously reserved line. + * @param line GPIO line object. + */ +void gpiod_line_release(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Release a set of previously reserved lines. + * @param bulk Set of GPIO lines to release. + */ +void gpiod_line_release_bulk(struct gpiod_line_bulk *bulk) GPIOD_API; + +/** + * @brief Check if the calling user has ownership of this line. + * @param line GPIO line object. + * @return True if given line was requested, false otherwise. + */ +bool gpiod_line_is_reserved(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if the calling user has neither requested ownership of this + * line nor configured any event notifications. + * @param line GPIO line object. + * @return True if given line is free, false otherwise. + */ +bool gpiod_line_is_free(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read current value of a single GPIO line. + * @param line GPIO line object. + * @return 0 or 1 if the operation succeeds. On error this routine returns -1 + * and sets the last error number. + */ +int gpiod_line_get_value(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read current values of a set of GPIO lines. + * @param bulk Set of GPIO lines to reserve. + * @param values An array big enough to hold line_bulk->num_lines values. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + * + * If succeeds, this routine fills the values array with a set of values in + * the same order, the lines are added to line_bulk. + */ +int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, + int *values) GPIOD_API; + +/** + * @brief Set the value of a single GPIO line. + * @param line GPIO line object. + * @param value New value. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + */ +int gpiod_line_set_value(struct gpiod_line *line, int value) GPIOD_API; + +/** + * @brief Set the values of a set of GPIO lines. + * @param bulk Set of GPIO lines to reserve. + * @param values An array holding line_bulk->num_lines new values for lines. + * @return 0 is the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + */ +int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, + int *values) GPIOD_API; + +/** + * @brief Find a GPIO line by its name. + * @param name Name of the GPIO line. + * @return Returns the GPIO line handle if the line exists in the system or + * NULL if it couldn't be located. + * + * If this routine succeeds, the user must manually close the GPIO chip owning + * this line to avoid memory leaks. + */ +struct gpiod_line * gpiod_line_find_by_name(const char *name) GPIOD_API; + +/** + * @brief Get the handle to the GPIO chip controlling this line. + * @param line The GPIO line object. + * @return Pointer to the GPIO chip handle controlling this line. + */ +struct gpiod_chip * gpiod_line_get_chip(struct gpiod_line *line) GPIOD_API; + +/** + * @defgroup __line_events__ Line event operations + * @{ + * + * Functions and data structures for requesting and reading GPIO line events. + */ + +/** + * @brief Event types. + */ +enum { + GPIOD_EVENT_RISING_EDGE, + /**< Rising edge event. */ + GPIOD_EVENT_FALLING_EDGE, + /**< Falling edge event. */ + GPIOD_EVENT_BOTH_EDGES, + /**< Rising or falling edge event: only relevant for event requests. */ +}; + +/** + * @brief Structure holding configuration of a line event request. + */ +struct gpiod_line_evreq_config { + const char *consumer; + /**< Name of the consumer. */ + int event_type; + /**< Type of the event we want to be notified about. */ + int active_state; + /**< GPIO line active state. */ + int line_flags; + /**< Misc line flags - same as for line requests. */ +}; + +/** + * @brief Structure holding event info. + */ +struct gpiod_line_event { + struct timespec ts; + /**< Best estimate of time of event occurrence. */ + int event_type; + /**< Type of the event that occurred. */ +}; + +/** + * @brief Request event notifications for a single line. + * @param line GPIO line object. + * @param config Event request configuration. + * @return 0 if the operation succeeds. In case of an error this routine + * returns -1 and sets the last error number. + */ +int gpiod_line_event_request(struct gpiod_line *line, + struct gpiod_line_evreq_config *config) GPIOD_API; + +/** + * @brief Request all event type notifications on a single line. + * @param line GPIO line object. + * @param consumer Name of the consumer. + * @param active_low Active state of the line - true if low. + * @return 0 if the operation succeeds, -1 on failure. + */ +static inline int gpiod_line_event_request_all(struct gpiod_line *line, + const char *consumer, + bool active_low) +{ + struct gpiod_line_evreq_config config = { + .consumer = consumer, + .event_type = GPIOD_EVENT_BOTH_EDGES, + .active_state = active_low ? GPIOD_ACTIVE_STATE_LOW + : GPIOD_ACTIVE_STATE_HIGH, + }; + + return gpiod_line_event_request(line, &config); +} + +/** + * @brief Stop listening for events and release the line. + * @param line GPIO line object. + */ +void gpiod_line_event_release(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Check if event notifications are configured on this line. + * @param line GPIO line object. + * @return True if event notifications are configured. False otherwise. + */ +bool gpiod_line_event_configured(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Wait for an event on a single line. + * @param line GPIO line object. + * @param timeout Wait time limit. + * @return 0 if wait timed out, -1 if an error occurred, 1 if an event + * occurred. + */ +int gpiod_line_event_wait(struct gpiod_line *line, + const struct timespec *timeout) GPIOD_API; + +/** + * @brief Wait for the first event on a set of lines. + * @param bulk Set of GPIO lines to monitor. + * @param timeout Wait time limit. + * @param line The handle of the line on which an event occurs is stored + * in this variable. Can be NULL. + * @return 0 if wait timed out, -1 if an error occurred, 1 if an event + * occurred. + */ +int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk, + const struct timespec *timeout, + struct gpiod_line **line) GPIOD_API; + +/** + * @brief Read the last event from the GPIO line. + * @param line GPIO line object. + * @param event Buffer to which the event data will be copied. + * @return 0 if the event was read correctly, -1 on error. + */ +int gpiod_line_event_read(struct gpiod_line *line, + struct gpiod_line_event *event) GPIOD_API; + +/** + * @brief Get the event file descriptor. + * @param line GPIO line object. + * @return Number of the event file descriptor or -1 on error. + * + * Users may want to poll the event file descriptor on their own. This routine + * allows to access it. + */ +int gpiod_line_event_get_fd(struct gpiod_line *line) GPIOD_API; + +/** + * @brief Read the last GPIO event directly from a file descriptor. + * @param fd File descriptor. + * @param event Buffer to which the event data will be copied. + * @return 0 if the event was read correctly, -1 on error. + * + * Users who directly poll the file descriptor for incoming events can also + * directly read the event data from it using this routine. This function + * translates the kernel representation of the event to the libgpiod format. + */ +int gpiod_line_event_read_fd(int fd, struct gpiod_line_event *event) GPIOD_API; + +/** + * @} + * + * @} + * + * @defgroup __chips__ GPIO chip operations + * @{ + * + * Functions and data structures dealing with GPIO chips. + */ + +/** + * @brief Open a gpiochip by path. + * @param path Path to the gpiochip device file. + * @return GPIO chip handle or NULL if an error occurred. + */ +struct gpiod_chip * gpiod_chip_open(const char *path) GPIOD_API; + +/** + * @brief Open a gpiochip by name. + * @param name Name of the gpiochip to open. + * @return GPIO chip handle or NULL if an error occurred. + * + * This routine appends name to '/dev/' to create the path. + */ +struct gpiod_chip * gpiod_chip_open_by_name(const char *name) GPIOD_API; + +/** + * @brief Open a gpiochip by number. + * @param num Number of the gpiochip. + * @return GPIO chip handle or NULL if an error occurred. + * + * This routine appends num to '/dev/gpiochip' to create the path. + */ +struct gpiod_chip * gpiod_chip_open_by_number(unsigned int num) GPIOD_API; + +/** + * @brief Open a gpiochip based on the best guess what the path is. + * @param descr String describing the gpiochip. + * @return GPIO chip handle or NULL if an error occurred. + * + * This routine tries to figure out whether the user passed it the path to + * the GPIO chip, its name or number as a string. Then it tries to open it + * using one of the other gpiod_chip_open** routines. + */ +struct gpiod_chip * gpiod_chip_open_lookup(const char *descr) GPIOD_API; + +/** + * @brief Close a GPIO chip handle and release all allocated resources. + * @param chip The GPIO chip object. + */ +void gpiod_chip_close(struct gpiod_chip *chip) GPIOD_API; + +/** + * @brief Get the GPIO chip name as represented in the kernel. + * @param chip The GPIO chip object. + * @return Pointer to a human-readable string containing the chip name. + */ +const char * gpiod_chip_name(struct gpiod_chip *chip) GPIOD_API; + +/** + * @brief Get the GPIO chip label as represented in the kernel. + * @param chip The GPIO chip object. + * @return Pointer to a human-readable string containing the chip label. + */ +const char * gpiod_chip_label(struct gpiod_chip *chip) GPIOD_API; + +/** + * @brief Get the number of GPIO lines exposed by this chip. + * @param chip The GPIO chip object. + * @return Number of GPIO lines. + */ +unsigned int gpiod_chip_num_lines(struct gpiod_chip *chip) GPIOD_API; + +/** + * @brief Get the handle to the GPIO line at given offset. + * @param chip The GPIO chip object. + * @param offset The offset of the GPIO line. + * @return Pointer to the GPIO line handle or NULL if an error occured. + */ +struct gpiod_line * +gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset) GPIOD_API; + +/** + * @} + * + * @defgroup __iterators__ Iterators for GPIO chips and lines + * @{ + * + * These functions and data structures allow easy iterating over GPIO + * chips and lines. + */ + +/** + * @brief Create a new gpiochip iterator. + * @return Pointer to a new chip iterator object or NULL if an error occurred. + */ +struct gpiod_chip_iter * gpiod_chip_iter_new(void) GPIOD_API; + +/** + * @brief Release all resources allocated for the gpiochip iterator and close + * the most recently opened gpiochip (if any). + * @param iter The gpiochip iterator object. + */ +void gpiod_chip_iter_free(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Release all resources allocated for the gpiochip iterator but + * don't close the most recently opened gpiochip (if any). + * @param iter The gpiochip iterator object. + * + * Users may want to break the loop when iterating over gpiochips and keep + * the most recently opened chip active while freeing the iterator data. + * This routine enables that. + */ +void gpiod_chip_iter_free_noclose(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Get the next gpiochip handle. + * @param iter The gpiochip iterator object. + * @return Pointer to an open gpiochip handle or NULL if the next chip can't + * be accessed. + * + * Internally this routine scans the /dev/ directory, storing current state + * in the chip iterator structure, and tries to open the next /dev/gpiochipX + * device file. If an error occurs or no more chips are present, the function + * returns NULL. + */ +struct gpiod_chip * +gpiod_chip_iter_next(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Iterate over all gpiochip present in the system. + * @param iter An initialized GPIO chip iterator. + * @param chip Pointer to a GPIO chip handle. On each iteration the newly + * opened chip handle is assigned to this argument. + * + * The user must not close the GPIO chip manually - instead the previous chip + * handle is closed automatically on the next iteration. The last chip to be + * opened is closed internally by gpiod_chip_iter_free(). + */ +#define gpiod_foreach_chip(iter, chip) \ + for ((chip) = gpiod_chip_iter_next(iter); \ + !gpiod_chip_iter_done(iter); \ + (chip) = gpiod_chip_iter_next(iter)) + +/** + * @brief Check if we're done iterating over gpiochips on this iterator. + * @param iter The gpiochip iterator object. + * @return True if we've iterated over all chips, false otherwise. + */ +bool gpiod_chip_iter_done(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Check if we've encountered an error condition while opening a + * gpiochip. + * @param iter The gpiochip iterator object. + * @return True if there was an error opening a gpiochip device file, + * false otherwise. + */ +bool gpiod_chip_iter_iserr(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Get the name of the gpiochip that we failed to access. + * @param iter The gpiochip iterator object. + * @return If gpiod_chip_iter_iserr() returned true, this function returns a + * pointer to the name of the gpiochip that we failed to access. + * If there was no error this function returns NULL. + * + * NOTE: this function will return NULL if the internal memory allocation + * fails. + */ +const char * +gpiod_chip_iter_failed_chip(struct gpiod_chip_iter *iter) GPIOD_API; + +/** + * @brief Possible states of a line iterator. + */ +enum { + GPIOD_LINE_ITER_INIT = 0, + /**< Line iterator is initiated or iterating over lines. */ + GPIOD_LINE_ITER_DONE, + /**< Line iterator is done with all lines on this chip. */ + GPIOD_LINE_ITER_ERR, + /**< There was an error retrieving info for a line. */ +}; + +/** + * @brief GPIO line iterator structure. + * + * This structure is used in conjunction with gpiod_line_iter_next() to + * iterate over all GPIO lines of a single GPIO chip. + */ +struct gpiod_line_iter { + unsigned int offset; + /**< Current line offset. */ + struct gpiod_chip *chip; + /**< GPIO chip whose line we're iterating over. */ + int state; + /**< Current state of the iterator. */ +}; + +/** + * @brief Static initializer for line iterators. + * @param chip The gpiochip object whose lines we want to iterate over. + */ +#define GPIOD_LINE_ITER_INITIALIZER(chip) \ + { \ + .offset = 0, \ + .chip = (chip), \ + .state = GPIOD_LINE_ITER_INIT, \ + } + +/** + * @brief Initialize a GPIO line iterator. + * @param iter Pointer to a GPIO line iterator. + * @param chip The gpiochip object whose lines we want to iterate over. + */ +static inline void gpiod_line_iter_init(struct gpiod_line_iter *iter, + struct gpiod_chip *chip) +{ + iter->offset = 0; + iter->chip = chip; + iter->state = GPIOD_LINE_ITER_INIT; +} + +/** + * @brief Get the next GPIO line handle. + * @param iter The GPIO line iterator object. + * @return Pointer to the next GPIO line handle or NULL if no more lines or + * and error occured. + */ +struct gpiod_line * +gpiod_line_iter_next(struct gpiod_line_iter *iter) GPIOD_API; + +/** + * @brief Check if we're done iterating over lines on this iterator. + * @param iter The GPIO line iterator object. + * @return True if we've iterated over all lines, false otherwise. + */ +static inline bool gpiod_line_iter_done(const struct gpiod_line_iter *iter) +{ + return iter->state == GPIOD_LINE_ITER_DONE; +} + +/** + * @brief Check if we've encountered an error condition while retrieving + * info for a line. + * @param iter The GPIO line iterator object. + * @return True if there was an error retrieving info about a GPIO line, + * false otherwise. + */ +static inline bool gpiod_line_iter_err(const struct gpiod_line_iter *iter) +{ + return iter->state == GPIOD_LINE_ITER_ERR; +} + +/** + * @brief Get the offset of the last line we tried to open. + * @param iter The GPIO line iterator object. + * @return The offset of the last line we tried to open - whether we failed + * or succeeded to do so. + */ +static inline unsigned int +gpiod_line_iter_last_offset(const struct gpiod_line_iter *iter) +{ + return iter->offset - 1; +} + +/** + * @brief Iterate over all GPIO lines of a single chip. + * @param iter An initialized GPIO line iterator. + * @param line Pointer to a GPIO line handle - on each iteration, the + * next GPIO line will be assigned to this argument. + */ +#define gpiod_foreach_line(iter, line) \ + for ((line) = gpiod_line_iter_next(iter); \ + !gpiod_line_iter_done(iter); \ + (line) = gpiod_line_iter_next(iter)) + +/** + * @} + * + * @defgroup __misc__ Stuff that didn't fit anywhere else + * @{ + * + * Various libgpiod-related functions. + */ + +/** + * @brief Get the version of the library as a human-readable string. + * @return Human-readable string containing the library version. + */ +const char * gpiod_version_string(void) GPIOD_API; + +/** + * @} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __LIBGPIOD_GPIOD_H__ */ diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..ccbe7dc --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,15 @@ +# +# Copyright (C) 2017 Bartosz Golaszewski +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# + +if WITH_TOOLS + +TOOLS_SUBDIR = tools + +endif + +SUBDIRS = . lib $(TOOLS_SUBDIR) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am new file mode 100644 index 0000000..35b8e70 --- /dev/null +++ b/src/lib/Makefile.am @@ -0,0 +1,14 @@ +# +# Copyright (C) 2017 Bartosz Golaszewski +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# + +lib_LTLIBRARIES = libgpiod.la +libgpiod_la_SOURCES = core.c +libgpiod_la_CFLAGS = -Wall -Wextra -g -D_GNU_SOURCE +libgpiod_la_CFLAGS += -fvisibility=hidden -I$(top_srcdir)/include/ +libgpiod_la_CFLAGS += -include $(top_srcdir)/config.h +libgpiod_la_LDFLAGS = -version-number $(subst .,:,$(PACKAGE_VERSION)) diff --git a/src/lib/core.c b/src/lib/core.c new file mode 100644 index 0000000..776ed9e --- /dev/null +++ b/src/lib/core.c @@ -0,0 +1,1084 @@ +/* + * GPIO chardev utils for linux. + * + * Copyright (C) 2017 Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpiod_chip +{ + int fd; + struct gpiochip_info cinfo; + struct gpiod_line *lines; +}; + +enum { + LINE_FREE = 0, + LINE_TAKEN, + LINE_EVENT, +}; + +struct handle_data { + struct gpiohandle_request request; + int refcount; +}; + +struct gpiod_line { + int state; + bool up_to_date; + struct gpiod_chip *chip; + struct gpioline_info info; + union { + struct handle_data *handle; + struct gpioevent_request event; + }; +}; + +enum { + CHIP_ITER_INIT = 0, + CHIP_ITER_DONE, + CHIP_ITER_ERR, +}; + +struct gpiod_chip_iter +{ + DIR *dir; + struct gpiod_chip *current; + int state; + char *failed_chip; +}; + +static const char dev_dir[] = "/dev/"; +static const char cdev_prefix[] = "gpiochip"; +static const char libgpiod_consumer[] = "libgpiod"; + +/* + * The longest error message in glibc is about 50 characters long so 64 should + * be enough to store every error message in the future too. + */ +#define ERRSTR_MAX 64 + +static __thread int last_error; +static __thread char errmsg[ERRSTR_MAX]; + +static const char *const error_descr[] = { + "success", + "GPIO line not reserved", + "no events configured on GPIO line", + "GPIO lines in bulk don't belong to the same gpiochip", + "GPIO line currently in use", +}; + +static void set_last_error(int errnum) +{ + last_error = errnum; +} + +static void last_error_from_errno(void) +{ + last_error = errno; +} + +static void * zalloc(size_t size) +{ + void *ptr; + + ptr = malloc(size); + if (!ptr) { + set_last_error(ENOMEM); + return NULL; + } + + memset(ptr, 0, size); + + return ptr; +} + +static bool is_unsigned_int(const char *str) +{ + for (; *str && isdigit(*str); str++); + + return *str == '\0'; +} + +static void nsec_to_timespec(uint64_t nsec, struct timespec *ts) +{ + ts->tv_sec = nsec / 1000000000ULL; + ts->tv_nsec = (nsec % 1000000000ULL); +} + +static int gpio_ioctl(int fd, unsigned long request, void *data) +{ + int status; + + status = ioctl(fd, request, data); + if (status < 0) { + last_error_from_errno(); + return -1; + } + + return 0; +} + +int gpiod_errno(void) +{ + return last_error; +} + +const char * gpiod_strerror(int errnum) +{ + if (errnum < __GPIOD_ERRNO_OFFSET) + return strerror_r(errnum, errmsg, sizeof(errmsg)); + else if (errnum > __GPIOD_MAX_ERR) + return "invalid error number"; + else + return error_descr[errnum - __GPIOD_ERRNO_OFFSET]; +} + +const char * gpiod_last_strerror(void) +{ + return gpiod_strerror(gpiod_errno()); +} + +int gpiod_simple_get_value(const char *device, + unsigned int offset, bool active_low) +{ + struct gpiod_chip *chip; + struct gpiod_line *line; + int status, value; + + chip = gpiod_chip_open_lookup(device); + if (!chip) + return -1; + + line = gpiod_chip_get_line(chip, offset); + if (!line) { + gpiod_chip_close(chip); + return -1; + } + + status = gpiod_line_request_input(line, libgpiod_consumer, active_low); + if (status < 0) { + gpiod_chip_close(chip); + return -1; + } + + value = gpiod_line_get_value(line); + + gpiod_line_release(line); + gpiod_chip_close(chip); + + return value; +} + +int gpiod_simple_set_value(const char *device, unsigned int offset, int value, + bool active_low, void (*cb)(void *), void *data) +{ + struct gpiod_chip *chip; + struct gpiod_line *line; + int status; + + chip = gpiod_chip_open_lookup(device); + if (!chip) + return -1; + + line = gpiod_chip_get_line(chip, offset); + if (!line) { + gpiod_chip_close(chip); + return -1; + } + + status = gpiod_line_request_output(line, libgpiod_consumer, + active_low, value); + if (status < 0) { + gpiod_chip_close(chip); + return -1; + } + + if (cb) + cb(data); + + gpiod_line_release(line); + gpiod_chip_close(chip); + + return 0; +} + +int gpiod_simple_event_loop(const char *device, unsigned int offset, + bool active_low, struct timespec *timeout, + gpiod_event_cb callback, void *cbdata) +{ + struct gpiod_line_event event; + struct gpiod_chip *chip; + struct gpiod_line *line; + int status, evtype; + + chip = gpiod_chip_open_lookup(device); + if (!chip) + return -1; + + line = gpiod_chip_get_line(chip, offset); + if (!line) { + gpiod_chip_close(chip); + return -1; + } + + status = gpiod_line_event_request_all(line, + libgpiod_consumer, active_low); + if (status < 0) { + gpiod_chip_close(chip); + return -1; + } + + for (;;) { + status = gpiod_line_event_wait(line, timeout); + if (status < 0) { + if (gpiod_errno() == EINTR) + return evtype = GPIOD_EVENT_CB_TIMEOUT; + else + goto out; + } else if (status == 0) { + evtype = GPIOD_EVENT_CB_TIMEOUT; + } else { + status = gpiod_line_event_read(line, &event); + if (status < 0) + goto out; + + evtype = event.event_type == GPIOD_EVENT_RISING_EDGE + ? GPIOD_EVENT_CB_RISING_EDGE + : GPIOD_EVENT_CB_FALLING_EDGE; + } + + status = callback(evtype, &event.ts, cbdata); + if (status == GPIOD_EVENT_CB_STOP) + goto out; + } + +out: + gpiod_line_event_release(line); + gpiod_chip_close(chip); + + return status; +} + +static void line_set_offset(struct gpiod_line *line, unsigned int offset) +{ + line->info.line_offset = offset; +} + +static int line_get_state(struct gpiod_line *line) +{ + return line->state; +} + +static void line_set_state(struct gpiod_line *line, int state) +{ + line->state = state; +} + +static int line_get_handle_fd(struct gpiod_line *line) +{ + if (line_get_state(line) != LINE_TAKEN) + return -1; + else + return line->handle->request.fd; +} + +static int line_get_event_fd(struct gpiod_line *line) +{ + if (line_get_state(line) != LINE_EVENT) + return -1; + else + return line->event.fd; +} + +static void line_set_handle(struct gpiod_line *line, + struct handle_data *handle) +{ + line->handle = handle; + handle->refcount++; +} + +static void line_remove_handle(struct gpiod_line *line) +{ + struct handle_data *handle; + + if (!line->handle) + return; + + handle = line->handle; + line->handle = NULL; + handle->refcount--; + if (handle->refcount <= 0) { + close(handle->request.fd); + free(handle); + } +} + +unsigned int gpiod_line_offset(struct gpiod_line *line) +{ + return (unsigned int)line->info.line_offset; +} + +const char * gpiod_line_name(struct gpiod_line *line) +{ + return line->info.name[0] == '\0' ? NULL : line->info.name; +} + +const char * gpiod_line_consumer(struct gpiod_line *line) +{ + return line->info.consumer[0] == '\0' ? NULL : line->info.consumer; +} + +int gpiod_line_direction(struct gpiod_line *line) +{ + return line->info.flags & GPIOLINE_FLAG_IS_OUT ? GPIOD_DIRECTION_OUTPUT + : GPIOD_DIRECTION_INPUT; +} + +int gpiod_line_active_state(struct gpiod_line *line) +{ + return line->info.flags & GPIOLINE_FLAG_ACTIVE_LOW + ? GPIOD_ACTIVE_STATE_LOW + : GPIOD_ACTIVE_STATE_HIGH; +} + +bool gpiod_line_is_used_by_kernel(struct gpiod_line *line) +{ + return line->info.flags & GPIOLINE_FLAG_KERNEL; +} + +bool gpiod_line_is_open_drain(struct gpiod_line *line) +{ + return line->info.flags & GPIOLINE_FLAG_OPEN_DRAIN; +} + +bool gpiod_line_is_open_source(struct gpiod_line *line) +{ + return line->info.flags & GPIOLINE_FLAG_OPEN_SOURCE; +} + +static void line_set_updated(struct gpiod_line *line) +{ + line->up_to_date = true; +} + +static void line_set_needs_update(struct gpiod_line *line) +{ + line->up_to_date = false; +} + +static void line_update(struct gpiod_line *line) +{ + int status; + + status = gpiod_line_update(line); + if (status < 0) + line_set_needs_update(line); +} + +bool gpiod_line_needs_update(struct gpiod_line *line) +{ + return !line->up_to_date; +} + +int gpiod_line_update(struct gpiod_line *line) +{ + struct gpiod_chip *chip; + int status, fd; + + memset(line->info.name, 0, sizeof(line->info.name)); + memset(line->info.consumer, 0, sizeof(line->info.consumer)); + line->info.flags = 0; + + chip = gpiod_line_get_chip(line); + fd = chip->fd; + + status = gpio_ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line->info); + if (status < 0) + return -1; + + line_set_updated(line); + + 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 verify_line_bulk(struct gpiod_line_bulk *line_bulk) +{ + struct gpiod_chip *chip; + unsigned int i; + + chip = gpiod_line_get_chip(line_bulk->lines[0]); + + for (i = 1; i < line_bulk->num_lines; i++) { + if (chip != gpiod_line_get_chip(line_bulk->lines[i])) { + set_last_error(GPIOD_EBULKINCOH); + return false; + } + + if (!gpiod_line_is_free(line_bulk->lines[i])) { + set_last_error(GPIOD_ELINEBUSY); + return false; + } + } + + return true; +} + +int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk, + const struct gpiod_line_request_config *config, + int *default_vals) +{ + struct gpiohandle_request *req; + struct handle_data *handle; + struct gpiod_chip *chip; + struct gpiod_line *line; + int status, fd; + unsigned int i; + + if (!verify_line_bulk(bulk)) + return -1; + + handle = zalloc(sizeof(*handle)); + if (!handle) + return -1; + + req = &handle->request; + + if (config->flags & GPIOD_REQUEST_OPEN_DRAIN) + req->flags |= GPIOHANDLE_REQUEST_OPEN_DRAIN; + if (config->flags & GPIOD_REQUEST_OPEN_SOURCE) + req->flags |= GPIOHANDLE_REQUEST_OPEN_SOURCE; + + if (config->direction == GPIOD_DIRECTION_INPUT) + req->flags |= GPIOHANDLE_REQUEST_INPUT; + else if (config->direction == GPIOD_DIRECTION_OUTPUT) + req->flags |= GPIOHANDLE_REQUEST_OUTPUT; + + if (config->active_state == GPIOD_ACTIVE_STATE_LOW) + req->flags |= GPIOHANDLE_REQUEST_ACTIVE_LOW; + + req->lines = bulk->num_lines; + + for (i = 0; i < bulk->num_lines; i++) { + req->lineoffsets[i] = gpiod_line_offset(bulk->lines[i]); + if (config->direction == GPIOD_DIRECTION_OUTPUT) + req->default_values[i] = !!default_vals[i]; + } + + strncpy(req->consumer_label, config->consumer, + sizeof(req->consumer_label) - 1); + + chip = gpiod_line_get_chip(bulk->lines[0]); + fd = chip->fd; + + status = gpio_ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, req); + if (status < 0) + return -1; + + for (i = 0; i < bulk->num_lines; i++) { + line = bulk->lines[i]; + + line_set_handle(line, handle); + line_set_state(line, LINE_TAKEN); + line_update(line); + } + + return 0; +} + +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; + unsigned int i; + + for (i = 0; i < bulk->num_lines; i++) { + line = bulk->lines[i]; + + line_remove_handle(line); + line_set_state(line, LINE_FREE); + line_update(line); + } +} + +bool gpiod_line_is_reserved(struct gpiod_line *line) +{ + return line_get_state(line) == LINE_TAKEN; +} + +bool gpiod_line_is_free(struct gpiod_line *line) +{ + return line_get_state(line) == LINE_FREE; +} + +static bool line_bulk_is_reserved(struct gpiod_line_bulk *line_bulk) +{ + unsigned int i; + + for (i = 0; i < line_bulk->num_lines; i++) { + if (!gpiod_line_is_reserved(line_bulk->lines[i])) + return false; + } + + return true; +} + +int gpiod_line_get_value(struct gpiod_line *line) +{ + struct gpiod_line_bulk bulk; + int status, value; + + gpiod_line_bulk_init(&bulk); + gpiod_line_bulk_add(&bulk, line); + + status = gpiod_line_get_value_bulk(&bulk, &value); + if (status < 0) + return -1; + + return value; +} + +int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, int *values) +{ + struct gpiohandle_data data; + unsigned int i; + int status; + + if (!line_bulk_is_reserved(bulk)) { + set_last_error(GPIOD_EREQUEST); + return -1; + } + + memset(&data, 0, sizeof(data)); + + status = gpio_ioctl(line_get_handle_fd(bulk->lines[0]), + GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); + if (status < 0) + return -1; + + for (i = 0; i < bulk->num_lines; 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, int *values) +{ + struct gpiohandle_data data; + unsigned int i; + int status; + + if (!line_bulk_is_reserved(bulk)) { + set_last_error(GPIOD_EREQUEST); + return -1; + } + + memset(&data, 0, sizeof(data)); + + for (i = 0; i < bulk->num_lines; i++) + data.values[i] = (__u8)!!values[i]; + + status = gpio_ioctl(line_get_handle_fd(bulk->lines[0]), + GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data); + if (status < 0) + return -1; + + return 0; +} + +struct gpiod_line * gpiod_line_find_by_name(const char *name) +{ + struct gpiod_chip_iter *chip_iter; + struct gpiod_line_iter line_iter; + struct gpiod_chip *chip; + struct gpiod_line *line; + const char *line_name; + + chip_iter = gpiod_chip_iter_new(); + if (!chip_iter) + return NULL; + + gpiod_foreach_chip(chip_iter, chip) { + gpiod_line_iter_init(&line_iter, chip); + gpiod_foreach_line(&line_iter, line) { + line_name = gpiod_line_name(line); + if (!line_name) + continue; + + if (strcmp(gpiod_line_name(line), name) == 0) { + gpiod_chip_iter_free_noclose(chip_iter); + return line; + } + } + } + + gpiod_chip_iter_free(chip_iter); + + return NULL; +} + +int gpiod_line_event_request(struct gpiod_line *line, + struct gpiod_line_evreq_config *config) +{ + struct gpioevent_request *req; + struct gpiod_chip *chip; + int status, fd; + + if (!gpiod_line_is_free(line)) { + set_last_error(GPIOD_ELINEBUSY); + return -1; + } + + req = &line->event; + + memset(req, 0, sizeof(*req)); + strncpy(req->consumer_label, config->consumer, + sizeof(req->consumer_label) - 1); + req->lineoffset = gpiod_line_offset(line); + req->handleflags |= GPIOHANDLE_REQUEST_INPUT; + + if (config->line_flags & GPIOD_REQUEST_OPEN_DRAIN) + req->handleflags |= GPIOHANDLE_REQUEST_OPEN_DRAIN; + if (config->line_flags & GPIOD_REQUEST_OPEN_SOURCE) + req->handleflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE; + + if (config->active_state == GPIOD_ACTIVE_STATE_LOW) + req->handleflags |= GPIOHANDLE_REQUEST_ACTIVE_LOW; + + if (config->event_type == GPIOD_EVENT_RISING_EDGE) + req->eventflags |= GPIOEVENT_EVENT_RISING_EDGE; + else if (config->event_type == GPIOD_EVENT_FALLING_EDGE) + req->eventflags |= GPIOEVENT_EVENT_FALLING_EDGE; + else if (req->eventflags |= GPIOD_EVENT_BOTH_EDGES) + req->eventflags |= GPIOEVENT_REQUEST_BOTH_EDGES; + + chip = gpiod_line_get_chip(line); + fd = chip->fd; + + status = gpio_ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, req); + if (status < 0) + return -1; + + line_set_state(line, LINE_EVENT); + + return 0; +} + +void gpiod_line_event_release(struct gpiod_line *line) +{ + close(line_get_event_fd(line)); + line_set_state(line, LINE_FREE); +} + +bool gpiod_line_event_configured(struct gpiod_line *line) +{ + return line_get_state(line) == LINE_EVENT; +} + +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); +} + +static bool line_bulk_is_event_configured(struct gpiod_line_bulk *line_bulk) +{ + unsigned int i; + + for (i = 0; i < line_bulk->num_lines; i++) { + if (!gpiod_line_event_configured(line_bulk->lines[i])) + return false; + } + + return true; +} + +int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk, + const struct timespec *timeout, + struct gpiod_line **line) +{ + struct pollfd fds[GPIOD_REQUEST_MAX_LINES]; + struct gpiod_line *linetmp; + unsigned int i; + int status; + + if (!line_bulk_is_event_configured(bulk)) { + set_last_error(GPIOD_EEVREQUEST); + return -1; + } + + memset(fds, 0, sizeof(fds)); + + for (i = 0; i < bulk->num_lines; i++) { + linetmp = bulk->lines[i]; + + fds[i].fd = line_get_event_fd(linetmp); + fds[i].events = POLLIN | POLLPRI; + } + + status = ppoll(fds, bulk->num_lines, timeout, NULL); + if (status < 0) { + last_error_from_errno(); + return -1; + } else if (status == 0) { + return 0; + } + + for (i = 0; !fds[i].revents; i++); + if (line) + *line = bulk->lines[i]; + + return 1; +} + +int gpiod_line_event_read(struct gpiod_line *line, + struct gpiod_line_event *event) +{ + int fd; + + if (!gpiod_line_event_configured(line)) { + set_last_error(GPIOD_EEVREQUEST); + return -1; + } + + fd = line_get_event_fd(line); + + return gpiod_line_event_read_fd(fd, event); +} + +int gpiod_line_event_get_fd(struct gpiod_line *line) +{ + return line_get_state(line) == LINE_EVENT + ? line_get_event_fd(line) : -1; +} + +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) { + last_error_from_errno(); + return -1; + } else if (rd != sizeof(evdata)) { + set_last_error(EIO); + return -1; + } + + event->event_type = evdata.id == GPIOEVENT_EVENT_RISING_EDGE + ? GPIOD_EVENT_RISING_EDGE + : GPIOD_EVENT_FALLING_EDGE; + nsec_to_timespec(evdata.timestamp, &event->ts); + + return 0; +} + +struct gpiod_chip * gpiod_chip_open(const char *path) +{ + struct gpiod_chip *chip; + int status, fd; + + fd = open(path, O_RDWR); + if (fd < 0) { + last_error_from_errno(); + return NULL; + } + + chip = zalloc(sizeof(*chip)); + if (!chip) { + close(fd); + return NULL; + } + + chip->fd = fd; + + status = gpio_ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip->cinfo); + if (status < 0) { + close(chip->fd); + free(chip); + return NULL; + } + + chip->lines = zalloc(chip->cinfo.lines * sizeof(*chip->lines)); + if (!chip->lines) { + close(chip->fd); + free(chip); + return NULL; + } + + return chip; +} + +struct gpiod_chip * gpiod_chip_open_by_name(const char *name) +{ + struct gpiod_chip *chip; + char *path; + int status; + + status = asprintf(&path, "%s%s", dev_dir, name); + if (status < 0) { + last_error_from_errno(); + 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 status; + + status = asprintf(&path, "%s%s%u", dev_dir, cdev_prefix, num); + if (!status) { + last_error_from_errno(); + return NULL; + } + + chip = gpiod_chip_open(path); + free(path); + + return chip; +} + +struct gpiod_chip * gpiod_chip_open_lookup(const char *descr) +{ + if (is_unsigned_int(descr)) + return gpiod_chip_open_by_number(strtoul(descr, NULL, 10)); + else if (strncmp(descr, dev_dir, sizeof(dev_dir) - 1) != 0) + return gpiod_chip_open_by_name(descr); + else + return gpiod_chip_open(descr); +} + +void gpiod_chip_close(struct gpiod_chip *chip) +{ + unsigned int i; + + for (i = 0; i < chip->cinfo.lines; i++) { + if (chip->lines[i].state == LINE_TAKEN) + gpiod_line_release(&chip->lines[i]); + else if (chip->lines[i].state == LINE_EVENT) + gpiod_line_event_release(&chip->lines[i]); + } + + close(chip->fd); + free(chip->lines); + free(chip); +} + +const char * gpiod_chip_name(struct gpiod_chip *chip) +{ + return chip->cinfo.name[0] == '\0' ? NULL : chip->cinfo.name; +} + +const char * gpiod_chip_label(struct gpiod_chip *chip) +{ + return chip->cinfo.label[0] == '\0' ? NULL : chip->cinfo.label; +} + +unsigned int gpiod_chip_num_lines(struct gpiod_chip *chip) +{ + return (unsigned int)chip->cinfo.lines; +} + +struct gpiod_line * +gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset) +{ + struct gpiod_line *line; + int status; + + if (offset >= chip->cinfo.lines) { + set_last_error(EINVAL); + return NULL; + } + + line = &chip->lines[offset]; + line_set_offset(line, offset); + line->chip = chip; + + status = gpiod_line_update(line); + if (status < 0) + return NULL; + + return line; +} + +struct gpiod_chip * gpiod_line_get_chip(struct gpiod_line *line) +{ + return line->chip; +} + +struct gpiod_chip_iter * gpiod_chip_iter_new(void) +{ + struct gpiod_chip_iter *new; + + new = zalloc(sizeof(*new)); + if (!new) + return NULL; + + new->dir = opendir(dev_dir); + if (!new->dir) { + last_error_from_errno(); + return NULL; + } + + new->state = CHIP_ITER_INIT; + + return new; +} + +void gpiod_chip_iter_free(struct gpiod_chip_iter *iter) +{ + if (iter->current) + gpiod_chip_close(iter->current); + + gpiod_chip_iter_free_noclose(iter); +} + +void gpiod_chip_iter_free_noclose(struct gpiod_chip_iter *iter) +{ + closedir(iter->dir); + if (iter->failed_chip) + free(iter->failed_chip); + free(iter); +} + +struct gpiod_chip * gpiod_chip_iter_next(struct gpiod_chip_iter *iter) +{ + struct gpiod_chip *chip; + struct dirent *dentry; + int status; + + if (iter->current) { + gpiod_chip_close(iter->current); + iter->current = NULL; + } + + for (dentry = readdir(iter->dir); + dentry; + dentry = readdir(iter->dir)) { + status = strncmp(dentry->d_name, cdev_prefix, + sizeof(cdev_prefix) - 1); + if (status == 0) { + iter->state = CHIP_ITER_INIT; + if (iter->failed_chip) { + free(iter->failed_chip); + iter->failed_chip = NULL; + } + + chip = gpiod_chip_open_by_name(dentry->d_name); + if (!chip) { + iter->state = CHIP_ITER_ERR; + iter->failed_chip = strdup(dentry->d_name); + /* No point in an error check here. */ + } + + iter->current = chip; + return iter->current; + } + } + + iter->state = CHIP_ITER_DONE; + return NULL; +} + +bool gpiod_chip_iter_done(struct gpiod_chip_iter *iter) +{ + return iter->state == CHIP_ITER_DONE; +} + +bool gpiod_chip_iter_iserr(struct gpiod_chip_iter *iter) +{ + return iter->state == CHIP_ITER_ERR; +} + +const char * +gpiod_chip_iter_failed_chip(struct gpiod_chip_iter *iter) +{ + return iter->failed_chip; +} + +struct gpiod_line * gpiod_line_iter_next(struct gpiod_line_iter *iter) +{ + struct gpiod_line *line; + + if (iter->offset >= gpiod_chip_num_lines(iter->chip)) { + iter->state = GPIOD_LINE_ITER_DONE; + return NULL; + } + + iter->state = GPIOD_LINE_ITER_INIT; + line = gpiod_chip_get_line(iter->chip, iter->offset++); + if (!line) + iter->state = GPIOD_LINE_ITER_ERR; + + return line; +} + +const char * gpiod_version_string(void) +{ + return PACKAGE_VERSION; +} diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am new file mode 100644 index 0000000..35e5f22 --- /dev/null +++ b/src/tools/Makefile.am @@ -0,0 +1,26 @@ +# +# Copyright (C) 2017 Bartosz Golaszewski +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# + +AM_CFLAGS = -I$(top_srcdir)/include/ -include $(top_srcdir)/config.h +AM_CFLAGS += -Wall -Wextra -g -D_GNU_SOURCE -L$(top_srcdir)/src/lib +AM_LDFLAGS = -lgpiod +DEPENDENCIES = libgpiod.la + +bin_PROGRAMS = gpiodetect gpioinfo gpioget gpioset gpiomon gpiofind + +gpiodetect_SOURCES = gpiodetect.c tools-common.c + +gpioinfo_SOURCES = gpioinfo.c tools-common.c + +gpioget_SOURCES = gpioget.c tools-common.c + +gpioset_SOURCES = gpioset.c tools-common.c + +gpiomon_SOURCES = gpiomon.c tools-common.c + +gpiofind_SOURCES = gpiofind.c tools-common.c diff --git a/src/tools/gpiodetect.c b/src/tools/gpiodetect.c new file mode 100644 index 0000000..6291340 --- /dev/null +++ b/src/tools/gpiodetect.c @@ -0,0 +1,86 @@ +/* + * List all GPIO chips. + * + * Copyright (C) 2017 Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include +#include "tools-common.h" + +#include +#include +#include + +static const struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { 0 }, +}; + +static const char *const shortopts = "+hv"; + +static void print_help(void) +{ + printf("Usage: %s \n", get_progname()); + printf("List all GPIO chips\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; + + set_progname(argv[0]); + + 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) { + if (gpiod_chip_iter_iserr(iter)) + die_perror("error accessing gpiochip %s", + gpiod_chip_iter_failed_chip(iter)); + + 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; +} diff --git a/src/tools/gpiofind.c b/src/tools/gpiofind.c new file mode 100644 index 0000000..0467f58 --- /dev/null +++ b/src/tools/gpiofind.c @@ -0,0 +1,79 @@ +/* + * Read value from GPIO line. + * + * Copyright (C) 2017 Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include +#include "tools-common.h" + +#include +#include +#include + +static const struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { 0 }, +}; + +static const char *const shortopts = "+hv"; + +static void print_help(void) +{ + printf("Usage: %s [NAME]\n", get_progname()); + printf("Find a GPIO line by name.\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_line *line; + struct gpiod_chip *chip; + int optc, opti; + + set_progname(argv[0]); + + 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("GPIO line name must be specified"); + + line = gpiod_line_find_by_name(argv[0]); + if (!line) + return EXIT_FAILURE; + + chip = gpiod_line_get_chip(line); + + printf("%s %u\n", gpiod_chip_name(chip), gpiod_line_offset(line)); + + gpiod_chip_close(chip); + + return EXIT_SUCCESS; +} diff --git a/src/tools/gpioget.c b/src/tools/gpioget.c new file mode 100644 index 0000000..b7d4ac9 --- /dev/null +++ b/src/tools/gpioget.c @@ -0,0 +1,91 @@ +/* + * Read value from GPIO line. + * + * Copyright (C) 2017 Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include +#include "tools-common.h" + +#include +#include +#include + +static const struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "active-low", no_argument, NULL, 'l' }, + { 0 }, +}; + +static const char *const shortopts = "hvl"; + +static void print_help(void) +{ + printf("Usage: %s [CHIP NAME/NUMBER] [LINE OFFSET] \n", + get_progname()); + printf("Read value from a GPIO line\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) +{ + bool active_low = false; + int value, optc, opti; + unsigned int offset; + char *device, *end; + + set_progname(argv[0]); + + 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("gpio line offset must be specified"); + + device = argv[0]; + + offset = strtoul(argv[1], &end, 10); + if (*end != '\0') + die("invalid GPIO offset: %s", argv[1]); + + value = gpiod_simple_get_value(device, offset, active_low); + if (value < 0) + die_perror("error reading GPIO value"); + + printf("%d\n", value); + + return EXIT_SUCCESS; +} diff --git a/src/tools/gpioinfo.c b/src/tools/gpioinfo.c new file mode 100644 index 0000000..d91c6ca --- /dev/null +++ b/src/tools/gpioinfo.c @@ -0,0 +1,211 @@ +/* + * List all lines of a GPIO chip. + * + * Copyright (C) 2017 Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include +#include "tools-common.h" + +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +struct flag { + const char *name; + bool (*is_set)(struct gpiod_line *); +}; + +static const struct flag flags[] = { + { + .name = "kernel", + .is_set = gpiod_line_is_used_by_kernel, + }, + { + .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' }, + { 0 }, +}; + +static const char *const shortopts = "+hv"; + +static void print_help(void) +{ + printf("Usage: %s [GPIOCHIP1...]\n", get_progname()); + printf("List all lines of a GPIO chip.\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 *overflow, + unsigned int pref_len, const char *fmt, ...) +{ + char *buf, *buffmt = NULL; + size_t len; + va_list va; + int status; + + va_start(va, fmt); + status = vasprintf(&buf, fmt, va); + va_end(va); + if (status < 0) + die("vasprintf: %s\n", strerror(errno)); + + len = strlen(buf) - 1; + + if (len >= pref_len || *overflow) { + *overflow = true; + printf("%s", buf); + } else { + status = asprintf(&buffmt, "%%%us", pref_len); + if (status < 0) + die("asprintf: %s\n", strerror(errno)); + + printf(buffmt, buf); + } + + free(buf); + if (fmt) + free(buffmt); +} + +static void list_lines(struct gpiod_chip *chip) +{ + bool flag_printed, overflow; + int direction, active_state; + struct gpiod_line_iter iter; + const char *name, *consumer; + struct gpiod_line *line; + unsigned int i, offset; + + printf("%s - %u lines:\n", + gpiod_chip_name(chip), gpiod_chip_num_lines(chip)); + + gpiod_line_iter_init(&iter, chip); + gpiod_foreach_line(&iter, line) { + if (gpiod_line_iter_err(&iter)) + die_perror("error retrieving info for line %u", + gpiod_line_iter_last_offset(&iter)); + + 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); + + overflow = false; + + printf("\tline "); + prinfo(&overflow, 2, "%u", offset); + printf(": "); + + name ? prinfo(&overflow, 12, "\"%s\"", name) + : prinfo(&overflow, 12, "unnamed"); + printf(" "); + + consumer ? prinfo(&overflow, 12, "\"%s\"", consumer) + : prinfo(&overflow, 12, "unused"); + printf(" "); + + prinfo(&overflow, 8, "%s ", direction == GPIOD_DIRECTION_INPUT + ? "input" : "output"); + prinfo(&overflow, 13, "%s ", + active_state == GPIOD_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"); + } +} + +int main(int argc, char **argv) +{ + struct gpiod_chip_iter *chip_iter; + struct gpiod_chip *chip; + int i, optc, opti; + + set_progname(argv[0]); + + 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) { + if (gpiod_chip_iter_iserr(chip_iter)) + die_perror("error accessing gpiochip %s", + gpiod_chip_iter_failed_chip(chip_iter)); + + 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; +} diff --git a/src/tools/gpiomon.c b/src/tools/gpiomon.c new file mode 100644 index 0000000..48eaa65 --- /dev/null +++ b/src/tools/gpiomon.c @@ -0,0 +1,130 @@ +/* + * Monitor events on a GPIO line. + * + * Copyright (C) 2017 Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include +#include "tools-common.h" + +#include +#include +#include +#include + +static const struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "active-low", no_argument, NULL, 'l' }, + { 0 }, +}; + +static const char *const shortopts = "hvl"; + +static void print_help(void) +{ + printf("Usage: %s [CHIP NAME/NUMBER] [LINE OFFSET] \n", + get_progname()); + printf("Wait for events on a GPIO line\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"); +} + +static volatile bool do_run = true; + +static void sighandler(int signum UNUSED) +{ + do_run = false; +} + +static int event_callback(int type, const struct timespec *ts, + void *data UNUSED) +{ + const char *evname = NULL; + + switch (type) { + case GPIOD_EVENT_CB_RISING_EDGE: + evname = " RISING EDGE"; + break; + case GPIOD_EVENT_CB_FALLING_EDGE: + evname = "FALLING_EDGE"; + break; + default: + break; + } + + if (evname) + printf("GPIO EVENT: %s [%8ld.%09ld]\n", + evname, ts->tv_sec, ts->tv_nsec); + + if (!do_run) + return GPIOD_EVENT_CB_STOP; + + return GPIOD_EVENT_CB_OK; +} + +int main(int argc, char **argv) +{ + bool active_low = false; + struct timespec timeout; + int optc, opti, status; + unsigned int offset; + char *device, *end; + + set_progname(argv[0]); + + 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("gpio line offset must be specified"); + + device = argv[0]; + offset = strtoul(argv[1], &end, 10); + if (*end != '\0') + die("invalid GPIO offset: %s", argv[1]); + + timeout.tv_sec = 0; + timeout.tv_nsec = 500000000; + + signal(SIGINT, sighandler); + signal(SIGTERM, sighandler); + + status = gpiod_simple_event_loop(device, offset, active_low, + &timeout, event_callback, NULL); + if (status < 0) + die_perror("error waiting for events"); + + return EXIT_SUCCESS; +} diff --git a/src/tools/gpioset.c b/src/tools/gpioset.c new file mode 100644 index 0000000..8ae288e --- /dev/null +++ b/src/tools/gpioset.c @@ -0,0 +1,104 @@ +/* + * Set value of a GPIO line. + * + * Copyright (C) 2017 Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include +#include "tools-common.h" + +#include +#include +#include + +static const struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "active-low", no_argument, NULL, 'l' }, + { 0 }, +}; + +static const char *const shortopts = "hvl"; + +static void print_help(void) +{ + printf("Usage: %s [CHIP NAME/NUMBER] [LINE OFFSET] [VALUE] \n", + get_progname()); + printf("Set value of a GPIO line\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"); + printf("This program reserves the GPIO line, sets its value and waits for the user to press ENTER before releasing the line\n"); +} + +static void wait_for_enter(void *data UNUSED) +{ + getchar(); +} + +int main(int argc, char **argv) +{ + int value, status, optc, opti; + bool active_low = false; + unsigned int offset; + char *device, *end; + + set_progname(argv[0]); + + 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("gpio line offset must be specified"); + + if (argc < 3) + die("value must be specified"); + + device = argv[0]; + + offset = strtoul(argv[1], &end, 10); + if (*end != '\0') + die("invalid GPIO offset: %s", argv[1]); + + value = strtoul(argv[2], &end, 10); + if (*end != '\0' || (value != 0 && value != 1)) + die("invalid value: %s", argv[2]); + + status = gpiod_simple_set_value(device, offset, value, + active_low, wait_for_enter, NULL); + if (status < 0) + die_perror("error setting the GPIO line value"); + + return EXIT_SUCCESS; +} diff --git a/src/tools/tools-common.c b/src/tools/tools-common.c new file mode 100644 index 0000000..a4c96fc --- /dev/null +++ b/src/tools/tools-common.c @@ -0,0 +1,78 @@ +/* + * Common code for GPIO tools. + * + * Copyright (C) 2017 Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#include +#include "tools-common.h" + +#include +#include +#include +#include +#include + +#define NORETURN __attribute__((noreturn)) + +static char *progname = "unknown"; + +void set_progname(char *name) +{ + progname = name; +} + +const char * get_progname(void) +{ + return progname; +} + +void NORETURN PRINTF(1, 2) die(const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + fprintf(stderr, "%s: ", progname); + vfprintf(stderr, fmt, va); + fprintf(stderr, "\n"); + va_end(va); + + exit(EXIT_FAILURE); +} + +void NORETURN PRINTF(1, 2) die_perror(const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + fprintf(stderr, "%s: ", progname); + vfprintf(stderr, fmt, va); + fprintf(stderr, ": %s\n", gpiod_last_strerror()); + va_end(va); + + exit(EXIT_FAILURE); +} + +void print_version(void) +{ + char *prog, *tmp; + + tmp = strdup(progname); + if (!tmp) + prog = progname; + else + prog = basename(tmp); + + printf("%s (libgpiod) %s\n", prog, gpiod_version_string()); + printf("Copyright (C) 2017 Bartosz Golaszewski\n"); + printf("License GPLv3+: GNU GPL version 3 or later\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"); + + if (tmp) + free(tmp); +} diff --git a/src/tools/tools-common.h b/src/tools/tools-common.h new file mode 100644 index 0000000..391c93b --- /dev/null +++ b/src/tools/tools-common.h @@ -0,0 +1,30 @@ +/* + * Common code for GPIO tools. + * + * Copyright (C) 2017 Bartosz Golaszewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + */ + +#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 UNUSED __attribute__((unused)) +#define PRINTF(fmt, arg) __attribute__((format(printf, fmt, arg))) + +void set_progname(char *name); +const char * get_progname(void); +void die(const char *fmt, ...); +void die_perror(const char *fmt, ...); +void print_version(void); + +#endif /* __GPIOD_TOOLS_COMMON_H__ */ diff --git a/tools-common.c b/tools-common.c deleted file mode 100644 index 1ba234a..0000000 --- a/tools-common.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Common code for GPIO tools. - * - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - */ - -#include "gpiod.h" -#include "tools-common.h" - -#include -#include -#include -#include -#include - -#define NORETURN __attribute__((noreturn)) - -static char *progname = "unknown"; - -void set_progname(char *name) -{ - progname = name; -} - -const char * get_progname(void) -{ - return progname; -} - -void NORETURN PRINTF(1, 2) die(const char *fmt, ...) -{ - va_list va; - - va_start(va, fmt); - fprintf(stderr, "%s: ", progname); - vfprintf(stderr, fmt, va); - fprintf(stderr, "\n"); - va_end(va); - - exit(EXIT_FAILURE); -} - -void NORETURN PRINTF(1, 2) die_perror(const char *fmt, ...) -{ - va_list va; - - va_start(va, fmt); - fprintf(stderr, "%s: ", progname); - vfprintf(stderr, fmt, va); - fprintf(stderr, ": %s\n", gpiod_last_strerror()); - va_end(va); - - exit(EXIT_FAILURE); -} - -void print_version(void) -{ - char *prog, *tmp; - - tmp = strdup(progname); - if (!tmp) - prog = progname; - else - prog = basename(tmp); - - printf("%s (libgpiod) %s\n", prog, gpiod_version_string()); - printf("Copyright (C) 2017 Bartosz Golaszewski\n"); - printf("License GPLv3+: GNU GPL version 3 or later\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"); - - if (tmp) - free(tmp); -} diff --git a/tools-common.h b/tools-common.h deleted file mode 100644 index 391c93b..0000000 --- a/tools-common.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Common code for GPIO tools. - * - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. - */ - -#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 UNUSED __attribute__((unused)) -#define PRINTF(fmt, arg) __attribute__((format(printf, fmt, arg))) - -void set_progname(char *name); -const char * get_progname(void); -void die(const char *fmt, ...); -void die_perror(const char *fmt, ...); -void print_version(void); - -#endif /* __GPIOD_TOOLS_COMMON_H__ */