build: create an organized directory structure
authorBartosz Golaszewski <bartekgola@gmail.com>
Sun, 15 Jan 2017 17:24:24 +0000 (18:24 +0100)
committerBartosz Golaszewski <bartekgola@gmail.com>
Sun, 15 Jan 2017 17:48:18 +0000 (18:48 +0100)
Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com>
26 files changed:
Makefile.am
configure.ac
core.c [deleted file]
gpiod.h [deleted file]
gpiodetect.c [deleted file]
gpiofind.c [deleted file]
gpioget.c [deleted file]
gpioinfo.c [deleted file]
gpiomon.c [deleted file]
gpioset.c [deleted file]
include/Makefile.am [new file with mode: 0644]
include/gpiod.h [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/lib/Makefile.am [new file with mode: 0644]
src/lib/core.c [new file with mode: 0644]
src/tools/Makefile.am [new file with mode: 0644]
src/tools/gpiodetect.c [new file with mode: 0644]
src/tools/gpiofind.c [new file with mode: 0644]
src/tools/gpioget.c [new file with mode: 0644]
src/tools/gpioinfo.c [new file with mode: 0644]
src/tools/gpiomon.c [new file with mode: 0644]
src/tools/gpioset.c [new file with mode: 0644]
src/tools/tools-common.c [new file with mode: 0644]
src/tools/tools-common.h [new file with mode: 0644]
tools-common.c [deleted file]
tools-common.h [deleted file]

index fa2c23ad7a4af046eb386e915eac3defd2f9f1ab..6151e59833456a818c8c111a1a246ba304290320 100644 (file)
@@ -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 -
index 5420682530e95d4b58bcfac13950fa3b23ca94ac..7520eb1246e6d078a29323ff999a44abf0429636 100644 (file)
@@ -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 (file)
index ddd19ae..0000000
--- a/core.c
+++ /dev/null
@@ -1,1084 +0,0 @@
-/*
- * GPIO chardev utils for linux.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * 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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <errno.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/poll.h>
-#include <linux/gpio.h>
-
-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 (file)
index f6cfefd..0000000
--- a/gpiod.h
+++ /dev/null
@@ -1,1029 +0,0 @@
-/*
- * GPIO chardev utils for linux.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * 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.
- *
- * <p>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 <stdlib.h>
-#include <stdbool.h>
-
-#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 (file)
index 507605a..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * List all GPIO chips.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * 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 <stdio.h>
-#include <string.h>
-#include <getopt.h>
-
-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 <options>\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 (file)
index f390672..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Read value from GPIO line.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * 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 <stdio.h>
-#include <string.h>
-#include <getopt.h>
-
-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 <options> [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 (file)
index 41ccb9c..0000000
--- a/gpioget.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Read value from GPIO line.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * 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 <stdio.h>
-#include <string.h>
-#include <getopt.h>
-
-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] <options>\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 (file)
index 8609b77..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * List all lines of a GPIO chip.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * 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 <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <getopt.h>
-#include <errno.h>
-
-#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 <options> [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 (file)
index 54da482..0000000
--- a/gpiomon.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Monitor events on a GPIO line.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * 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 <stdio.h>
-#include <string.h>
-#include <getopt.h>
-#include <signal.h>
-
-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] <options>\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 (file)
index 65dd70e..0000000
--- a/gpioset.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Set value of a GPIO line.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * 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 <stdio.h>
-#include <string.h>
-#include <getopt.h>
-
-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] <options>\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 (file)
index 0000000..74566a8
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+# 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 (file)
index 0000000..f6cfefd
--- /dev/null
@@ -0,0 +1,1029 @@
+/*
+ * GPIO chardev utils for linux.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * 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.
+ *
+ * <p>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 <stdlib.h>
+#include <stdbool.h>
+
+#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 (file)
index 0000000..ccbe7dc
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+# 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 (file)
index 0000000..35b8e70
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+# 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 (file)
index 0000000..776ed9e
--- /dev/null
@@ -0,0 +1,1084 @@
+/*
+ * GPIO chardev utils for linux.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <linux/gpio.h>
+
+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 (file)
index 0000000..35e5f22
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+# 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 (file)
index 0000000..6291340
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * List all GPIO chips.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <getopt.h>
+
+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 <options>\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 (file)
index 0000000..0467f58
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Read value from GPIO line.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <getopt.h>
+
+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 <options> [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 (file)
index 0000000..b7d4ac9
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Read value from GPIO line.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <getopt.h>
+
+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] <options>\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 (file)
index 0000000..d91c6ca
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * List all lines of a GPIO chip.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <getopt.h>
+#include <errno.h>
+
+#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 <options> [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 (file)
index 0000000..48eaa65
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Monitor events on a GPIO line.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+
+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] <options>\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 (file)
index 0000000..8ae288e
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Set value of a GPIO line.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <getopt.h>
+
+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] <options>\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 (file)
index 0000000..a4c96fc
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Common code for GPIO tools.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <libgen.h>
+
+#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 (file)
index 0000000..391c93b
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Common code for GPIO tools.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * 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 (file)
index 1ba234a..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Common code for GPIO tools.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * 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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <libgen.h>
-
-#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 (file)
index 391c93b..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Common code for GPIO tools.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * 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__ */