#
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 -
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],
[with_tools = false])
AM_CONDITIONAL([WITH_TOOLS], [test x$with_tools = xtrue])
+AM_PROG_AR
AC_PROG_CC
AC_PROG_LIBTOOL
AC_PROG_INSTALL
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
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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__ */
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+#
+# 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)
--- /dev/null
+#
+# 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))
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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__ */