!.gitignore
libgpiod.la
+libgpiodcxx.la
libtools-common.la
gpiodetect
gpioinfo
gpioset
gpiomon
gpiofind
+gpio_cxx_tests
+gpiodetectcxx
+gpiofindcxx
+gpiogetcxx
+gpioinfocxx
+gpiomoncxx
+gpiosetcxx
*.o
*.lo
doc
ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = foreign
-SUBDIRS = include src
+SUBDIRS = include src bindings
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libgpiod.pc
if HAS_DOXYGEN
doc:
- @(cat Doxyfile; echo PROJECT_NUMBER = $(VERSION_STR)) | doxygen -
+ @(cat Doxyfile; \
+ echo PROJECT_NUMBER = $(VERSION_STR); \
+ echo INPUT += bindings/cxx/gpiod.hpp) | doxygen -
.PHONY: doc
EXTRA_DIST = Doxyfile
--- /dev/null
+#
+# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or (at
+# your option) any later version.
+#
+
+SUBDIRS = .
+
+if WITH_BINDINGS_CXX
+
+SUBDIRS += cxx
+
+endif
--- /dev/null
+#
+# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or (at
+# your option) any later version.
+#
+
+lib_LTLIBRARIES = libgpiodcxx.la
+libgpiodcxx_la_SOURCES = chip.cpp iter.cpp line.cpp line_bulk.cpp
+libgpiodcxx_la_CPPFLAGS = -Wall -Wextra -g -std=gnu++11
+libgpiodcxx_la_CPPFLAGS += -fvisibility=hidden -I$(top_srcdir)/include/
+libgpiodcxx_la_LDFLAGS = -version-number $(subst .,:,$(PACKAGE_VERSION))
+libgpiodcxx_la_LDFLAGS += -lgpiod -L$(top_builddir)/src/lib
+
+include_HEADERS = gpiod.hpp
+
+SUBDIRS = . examples
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <gpiod.hpp>
+
+#include <system_error>
+#include <functional>
+#include <utility>
+#include <map>
+
+namespace gpiod {
+
+namespace {
+
+::gpiod_chip* open_lookup(const ::std::string& device)
+{
+ return ::gpiod_chip_open_lookup(device.c_str());
+}
+
+::gpiod_chip* open_by_path(const ::std::string& device)
+{
+ return ::gpiod_chip_open(device.c_str());
+}
+
+::gpiod_chip* open_by_name(const ::std::string& device)
+{
+ return ::gpiod_chip_open_by_name(device.c_str());
+}
+
+::gpiod_chip* open_by_label(const ::std::string& device)
+{
+ return ::gpiod_chip_open_by_label(device.c_str());
+}
+
+::gpiod_chip* open_by_number(const ::std::string& device)
+{
+ return ::gpiod_chip_open_by_number(::std::stoul(device));
+}
+
+using open_func = ::std::function<::gpiod_chip* (const ::std::string&)>;
+
+const ::std::map<int, open_func> open_funcs = {
+ { chip::OPEN_LOOKUP, open_lookup, },
+ { chip::OPEN_BY_PATH, open_by_path, },
+ { chip::OPEN_BY_NAME, open_by_name, },
+ { chip::OPEN_BY_LABEL, open_by_label, },
+ { chip::OPEN_BY_NUMBER, open_by_number, },
+};
+
+void chip_deleter(::gpiod_chip* chip)
+{
+ ::gpiod_chip_close(chip);
+}
+
+} /* namespace */
+
+chip::chip(const ::std::string& device, int how)
+ : _m_chip()
+{
+ this->open(device, how);
+}
+
+chip::chip(::gpiod_chip* chip)
+ : _m_chip(chip, chip_deleter)
+{
+
+}
+
+void chip::open(const ::std::string& device, int how)
+{
+ auto func = open_funcs.at(how);
+
+ ::gpiod_chip *chip = func(device);
+ if (!chip)
+ throw ::std::system_error(errno, ::std::system_category(),
+ "cannot open GPIO device " + device);
+
+ this->_m_chip.reset(chip, chip_deleter);
+}
+
+void chip::reset(void) noexcept
+{
+ this->_m_chip.reset();
+}
+
+::std::string chip::name(void) const
+{
+ this->throw_if_noref();
+
+ const char* name = ::gpiod_chip_name(this->_m_chip.get());
+
+ return ::std::move(name ? ::std::string(name) : ::std::string());
+}
+
+::std::string chip::label(void) const
+{
+ this->throw_if_noref();
+
+ const char* label = ::gpiod_chip_label(this->_m_chip.get());
+
+ return ::std::move(label ? ::std::string(label) : ::std::string());
+}
+
+unsigned int chip::num_lines(void) const
+{
+ this->throw_if_noref();
+
+ return ::gpiod_chip_num_lines(this->_m_chip.get());
+}
+
+line chip::get_line(unsigned int offset) const
+{
+ this->throw_if_noref();
+
+ if (offset >= this->num_lines())
+ throw ::std::out_of_range("line offset greater than the number of lines");
+
+ ::gpiod_line* line_handle = ::gpiod_chip_get_line(this->_m_chip.get(), offset);
+ if (!line_handle)
+ throw ::std::system_error(errno, ::std::system_category(),
+ "error getting GPIO line from chip");
+
+ return ::std::move(line(line_handle, *this));
+}
+
+line chip::find_line(const ::std::string& name) const
+{
+ this->throw_if_noref();
+
+ ::gpiod_line* handle = ::gpiod_chip_find_line(this->_m_chip.get(), name.c_str());
+ if (!handle && errno != ENOENT)
+ throw ::std::system_error(errno, ::std::system_category(),
+ "error looking up GPIO line by name");
+
+ return ::std::move(handle ? line(handle, *this) : line());
+}
+
+line_bulk chip::get_lines(const ::std::vector<unsigned int>& offsets) const
+{
+ line_bulk lines;
+
+ for (auto& it: offsets)
+ lines.add(this->get_line(it));
+
+ return ::std::move(lines);
+}
+
+line_bulk chip::find_lines(const ::std::vector<::std::string>& names) const
+{
+ line_bulk lines;
+
+ for (auto& it: names)
+ lines.add(this->find_line(it));
+
+ return ::std::move(lines);
+}
+
+bool chip::operator==(const chip& rhs) const noexcept
+{
+ return this->_m_chip.get() == rhs._m_chip.get();
+}
+
+bool chip::operator!=(const chip& rhs) const noexcept
+{
+ return this->_m_chip.get() != rhs._m_chip.get();
+}
+
+chip::operator bool(void) const noexcept
+{
+ return this->_m_chip.get() != nullptr;
+}
+
+bool chip::operator!(void) const noexcept
+{
+ return this->_m_chip.get() == nullptr;
+}
+
+void chip::throw_if_noref(void) const
+{
+ if (!this->_m_chip.get())
+ throw ::std::logic_error("object not associated with an open GPIO chip");
+}
+
+} /* namespace gpiod */
--- /dev/null
+#
+# Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or (at
+# your option) any later version.
+#
+
+AM_CPPFLAGS = -I$(top_srcdir)/bindings/cxx/ -I$(top_srcdir)/include
+AM_CPPFLAGS += -Wall -Wextra -g -std=gnu++11
+AM_LDFLAGS = -lgpiodcxx -L$(top_builddir)/bindings/cxx/
+
+check_PROGRAMS = gpio_cxx_tests \
+ gpiodetectcxx \
+ gpiofindcxx \
+ gpiogetcxx \
+ gpioinfocxx \
+ gpiomoncxx \
+ gpiosetcxx
+
+gpio_cxx_tests_SOURCES = gpio_cxx_tests.cpp
+
+gpiodetectcxx_SOURCES = gpiodetectcxx.cpp
+
+gpiofindcxx_SOURCES = gpiofindcxx.cpp
+
+gpiogetcxx_SOURCES = gpiogetcxx.cpp
+
+gpioinfocxx_SOURCES = gpioinfocxx.cpp
+
+gpiomoncxx_SOURCES = gpiomoncxx.cpp
+
+gpiosetcxx_SOURCES = gpiosetcxx.cpp
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* Misc tests/examples of the C++ API. */
+
+#include <gpiod.hpp>
+
+#include <stdexcept>
+#include <cstdlib>
+#include <iostream>
+
+namespace {
+
+using test_func = ::std::function<void (void)>;
+
+::std::vector<::std::pair<::std::string, test_func>> test_funcs;
+
+struct test_case
+{
+ test_case(const ::std::string& name, test_func& func)
+ {
+ test_funcs.push_back(::std::make_pair(name, func));
+ }
+};
+
+#define TEST_CASE(_func) \
+ test_func _test_func_##_func(_func); \
+ test_case _test_case_##_func(#_func, _test_func_##_func)
+
+void chip_info(void)
+{
+ ::gpiod::chip chip("gpiochip0");
+
+ ::std::cout << "chip name: " << chip.name() << ::std::endl;
+ ::std::cout << "chip label: " << chip.label() << ::std::endl;
+ ::std::cout << "number of lines " << chip.num_lines() << ::std::endl;
+}
+TEST_CASE(chip_info);
+
+void chip_open_different_modes(void)
+{
+ ::gpiod::chip by_name("gpiochip0", ::gpiod::chip::OPEN_BY_NAME);
+ ::gpiod::chip by_path("/dev/gpiochip0", ::gpiod::chip::OPEN_BY_PATH);
+ ::gpiod::chip by_label("gpio-mockup-A", ::gpiod::chip::OPEN_BY_LABEL);
+ ::gpiod::chip by_number("0", ::gpiod::chip::OPEN_BY_NUMBER);
+}
+TEST_CASE(chip_open_different_modes);
+
+void chip_line_ops(void)
+{
+ ::gpiod::chip chip("gpiochip0");
+
+ ::gpiod::line line = chip.get_line(3);
+ ::std::cout << "Got line by offset: " << line.offset() << ::std::endl;
+
+ line = chip.find_line("gpio-mockup-A-4");
+ ::std::cout << "Got line by name: " << line.name() << ::std::endl;
+
+ ::gpiod::line_bulk lines = chip.get_lines({ 1, 2, 3, 6, 6 });
+ ::std::cout << "Got multiple lines by offset: ";
+ for (auto& it: lines)
+ ::std::cout << it.offset() << " ";
+ ::std::cout << ::std::endl;
+
+ lines = chip.find_lines({ "gpio-mockup-A-1", "gpio-mockup-A-4", "gpio-mockup-A-7" });
+ ::std::cout << "Got multiple lines by name: ";
+ for (auto& it: lines)
+ ::std::cout << it.name() << " ";
+ ::std::cout << ::std::endl;
+}
+TEST_CASE(chip_line_ops);
+
+void line_info(void)
+{
+ ::gpiod::chip chip("gpiochip0");
+ ::gpiod::line line = chip.get_line(2);
+
+ ::std::cout << "line offset: " << line.offset() << ::std::endl;
+ ::std::cout << "line name: " << line.name() << ::std::endl;
+ ::std::cout << "line direction: "
+ << (line.direction() == ::gpiod::line::DIRECTION_INPUT ?
+ "input" : "output") << ::std::endl;
+}
+TEST_CASE(line_info);
+
+void empty_objects(void)
+{
+ ::std::cout << "Are initialized line & chip objects 'false'?" << ::std::endl;
+
+ ::gpiod::line line;
+ if (line)
+ throw ::std::logic_error("line built with a default constructor should be 'false'");
+
+ ::gpiod::chip chip;
+ if (chip)
+ throw ::std::logic_error("chip built with a default constructor should be 'false'");
+
+ ::std::cout << "YES" << ::std::endl;
+}
+TEST_CASE(empty_objects);
+
+void line_bulk_iterator(void)
+{
+ ::std::cout << "Checking line_bulk iterators" << ::std::endl;
+
+ ::gpiod::chip chip("gpiochip0");
+ ::gpiod::line_bulk bulk = chip.get_lines({ 0, 1, 2, 3, 4 });
+
+ for (auto& it: bulk)
+ ::std::cout << it.name() << ::std::endl;
+
+ ::std::cout << "DONE" << ::std::endl;
+}
+TEST_CASE(line_bulk_iterator);
+
+void single_line_test(void)
+{
+ const ::std::string line_name("gpio-mockup-A-4");
+
+ ::std::cout << "Looking up a GPIO line by name (" << line_name << ")" << ::std::endl;
+
+ ::gpiod::line line = ::gpiod::find_line(line_name);
+ if (!line)
+ throw ::std::runtime_error(line_name + " line not found");
+
+ ::std::cout << "Requesting a single line" << ::std::endl;
+
+ ::gpiod::line_request conf;
+ conf.consumer = "gpiod_cxx_tests";
+ conf.request_type = ::gpiod::line_request::DIRECTION_OUTPUT;
+
+ line.request(conf, 1);
+ ::std::cout << "Reading value" << ::std::endl;
+ ::std::cout << line.get_value() << ::std::endl;
+ ::std::cout << "Changing value to 0" << ::std::endl;
+ line.set_value(0);
+ ::std::cout << "Reading value again" << ::std::endl;
+ ::std::cout << line.get_value() << ::std::endl;
+}
+TEST_CASE(single_line_test);
+
+} /* namespace */
+
+int main(int, char **)
+{
+ for (auto& it: test_funcs) {
+ ::std::cout << "=================================================" << ::std::endl;
+ ::std::cout << it.first << ":\n" << ::std::endl;
+ it.second();
+ }
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* C++ reimplementation of the gpiodetect tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+ if (argc != 1) {
+ ::std::cerr << "usage: " << argv[0] << ::std::endl;
+ return EXIT_FAILURE;
+ }
+
+ for (auto& it: ::gpiod::make_chip_iterator()) {
+ ::std::cout << it.name() << " ["
+ << it.label() << "] ("
+ << it.num_lines() << " lines)" << ::std::endl;
+ }
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* C++ reimplementation of the gpiofind tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ ::std::cerr << "usage: " << argv[0] << " <line name>" << ::std::endl;
+ return EXIT_FAILURE;
+ }
+
+ ::gpiod::line line = ::gpiod::find_line(argv[1]);
+ if (!line)
+ return EXIT_FAILURE;
+
+ ::std::cout << line.get_chip().name() << " " << line.offset() << ::std::endl;
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* Simplified C++ reimplementation of the gpioget tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+ if (argc < 3) {
+ ::std::cerr << "usage: " << argv[0] << " <chip> <line_offset0> ..." << ::std::endl;
+ return EXIT_FAILURE;
+ }
+
+ ::std::vector<unsigned int> offsets;
+
+ for (int i = 2; i < argc; i++)
+ offsets.push_back(::std::stoul(argv[i]));
+
+ ::gpiod::chip chip(argv[1]);
+ auto lines = chip.get_lines(offsets);
+
+ lines.request({
+ argv[0],
+ ::gpiod::line_request::DIRECTION_INPUT,
+ 0
+ });
+
+ auto vals = lines.get_values();
+
+ for (auto& it: vals)
+ ::std::cout << it << ' ';
+ ::std::cout << ::std::endl;
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* Simplified C++ reimplementation of the gpioinfo tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+ if (argc != 1) {
+ ::std::cerr << "usage: " << argv[0] << ::std::endl;
+ return EXIT_FAILURE;
+ }
+
+ for (auto& cit: ::gpiod::make_chip_iterator()) {
+ ::std::cout << cit.name() << " - " << cit.num_lines() << " lines:" << ::std::endl;
+
+ for (auto& lit: ::gpiod::line_iterator(cit)) {
+ ::std::cout << "\tline ";
+ ::std::cout.width(3);
+ ::std::cout << lit.offset() << ": ";
+
+ ::std::cout.width(12);
+ ::std::cout << (lit.name().empty() ? "unnamed" : lit.name());
+ ::std::cout << " ";
+
+ ::std::cout.width(12);
+ ::std::cout << (lit.consumer().empty() ? "unused" : lit.consumer());
+ ::std::cout << " ";
+
+ ::std::cout.width(8);
+ ::std::cout << (lit.direction() == ::gpiod::line::DIRECTION_INPUT ? "input" : "output");
+ ::std::cout << " ";
+
+ ::std::cout.width(10);
+ ::std::cout << (lit.active_state() == ::gpiod::line::ACTIVE_LOW
+ ? "active-low" : "active-high");
+
+ ::std::cout << ::std::endl;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* Simplified C++ reimplementation of the gpiomon tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+namespace {
+
+void print_event(const ::gpiod::line_event& event)
+{
+ if (event.event_type == ::gpiod::line_event::RISING_EDGE)
+ ::std::cout << " RISING EDGE";
+ else if (event.event_type == ::gpiod::line_event::FALLING_EDGE)
+ ::std::cout << "FALLING EDGE";
+ else
+ throw ::std::logic_error("invalid event type");
+
+ ::std::cout << " ";
+
+ ::std::cout << ::std::chrono::duration_cast<::std::chrono::seconds>(event.timestamp).count();
+ ::std::cout << ".";
+ ::std::cout << event.timestamp.count() % 1000000000;
+
+ ::std::cout << " line: " << event.source.offset();
+
+ ::std::cout << ::std::endl;
+}
+
+} /* namespace */
+
+int main(int argc, char **argv)
+{
+ if (argc < 3) {
+ ::std::cout << "usage: " << argv[0] << " <chip> <offset0> ..." << ::std::endl;
+ return EXIT_FAILURE;
+ }
+
+ ::std::vector<unsigned int> offsets;
+ offsets.reserve(argc);
+ for (int i = 2; i < argc; i++)
+ offsets.push_back(::std::stoul(argv[i]));
+
+ ::gpiod::chip chip(argv[1]);
+ auto lines = chip.get_lines(offsets);
+
+ lines.request({
+ argv[0],
+ ::gpiod::line_request::EVENT_BOTH_EDGES,
+ 0,
+ });
+
+ for (;;) {
+ auto events = lines.event_wait(::std::chrono::nanoseconds(1000000000));
+ if (events) {
+ for (auto& it: events)
+ print_event(it.event_read());
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/* Simplified C++ reimplementation of the gpioset tool. */
+
+#include <gpiod.hpp>
+
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+ if (argc < 3) {
+ ::std::cerr << "usage: " << argv[0] << " <chip> <line_offset0>=<value0> ..." << ::std::endl;
+ return EXIT_FAILURE;
+ }
+
+ ::std::vector<unsigned int> offsets;
+ ::std::vector<int> values;
+
+ for (int i = 2; i < argc; i++) {
+ ::std::string arg(argv[i]);
+
+ size_t pos = arg.find('=');
+
+ ::std::string offset(arg.substr(0, pos));
+ ::std::string value(arg.substr(pos + 1, ::std::string::npos));
+
+ if (offset.empty() || value.empty())
+ throw ::std::invalid_argument("invalid argument: " + ::std::string(argv[i]));
+
+ offsets.push_back(::std::stoul(offset));
+ values.push_back(::std::stoul(value));
+ }
+
+ ::gpiod::chip chip(argv[1]);
+ auto lines = chip.get_lines(offsets);
+
+ lines.request({
+ argv[0],
+ ::gpiod::line_request::DIRECTION_OUTPUT,
+ 0
+ }, values);
+
+ ::std::cin.get();
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef __LIBGPIOD_GPIOD_CXX_HPP__
+#define __LIBGPIOD_GPIOD_CXX_HPP__
+
+#include <gpiod.h>
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <bitset>
+#include <chrono>
+
+namespace gpiod {
+
+class line;
+class line_bulk;
+class line_event;
+class line_iterator;
+class chip_iterator;
+
+/**
+ * @defgroup __gpiod_cxx__ C++ bindings
+ * @{
+ */
+
+/**
+ * @brief Represents a GPIO chip.
+ *
+ * Internally this class holds a smart pointer to an open GPIO chip descriptor.
+ * Multiple objects of this class can reference the same chip. The chip is
+ * closed and all resources freed when the last reference is dropped.
+ */
+class chip
+{
+public:
+
+ /**
+ * @brief Default constructor. Creates an empty GPIO chip object.
+ */
+ GPIOD_API chip(void) = default;
+
+ /**
+ * @brief Constructor. Opens the chip using chip::open.
+ * @param device String describing the GPIO chip.
+ * @param how Indicates how the chip should be opened.
+ */
+ GPIOD_API chip(const ::std::string& device, int how = OPEN_LOOKUP);
+
+ /**
+ * @brief Copy constructor. References the object held by other.
+ * @param other Other chip object.
+ */
+ GPIOD_API chip(const chip& other) = default;
+
+ /**
+ * @brief Move constructor. References the object held by other.
+ * @param other Other chip object.
+ */
+ GPIOD_API chip(chip&& other) = default;
+
+ /**
+ * @brief Assignment operator. References the object held by other.
+ * @param other Other chip object.
+ * @return Reference to this object.
+ */
+ GPIOD_API chip& operator=(const chip& other) = default;
+
+ /**
+ * @brief Move assignment operator. References the object held by other.
+ * @param other Other chip object.
+ * @return Reference to this object.
+ */
+ GPIOD_API chip& operator=(chip&& other) = default;
+
+ /**
+ * @brief Destructor. Unreferences the internal chip object.
+ */
+ GPIOD_API ~chip(void) = default;
+
+ /**
+ * @brief Open a GPIO chip.
+ * @param device String describing the GPIO chip.
+ * @param how Indicates how the chip should be opened.
+ *
+ * If the object already holds a reference to an open chip, it will be
+ * closed and the reference reset.
+ */
+ GPIOD_API void open(const ::std::string &device, int how = OPEN_LOOKUP);
+
+ /**
+ * @brief Reset the internal smart pointer owned by this object.
+ */
+ GPIOD_API void reset(void) noexcept;
+
+ /**
+ * @brief Return the name of the chip held by this object.
+ * @return Name of the GPIO chip.
+ */
+ GPIOD_API ::std::string name(void) const;
+
+ /**
+ * @brief Return the label of the chip held by this object.
+ * @return Label of the GPIO chip.
+ */
+ GPIOD_API ::std::string label(void) const;
+
+ /**
+ * @brief Return the number of lines exposed by this chip.
+ * @return Number of lines.
+ */
+ GPIOD_API unsigned int num_lines(void) const;
+
+ /**
+ * @brief Get the line exposed by this chip at given offset.
+ * @param offset Offset of the line.
+ * @return Line object.
+ */
+ GPIOD_API line get_line(unsigned int offset) const;
+
+ /**
+ * @brief Get the line exposed by this chip by name.
+ * @param name Line name.
+ * @return Line object.
+ */
+ GPIOD_API line find_line(const ::std::string& name) const;
+
+ /**
+ * @brief Get a set of lines exposed by this chip at given offsets.
+ * @param offsets Vector of line offsets.
+ * @return Set of lines held by a line_bulk object.
+ */
+ GPIOD_API line_bulk get_lines(const ::std::vector<unsigned int>& offsets) const;
+
+ /**
+ * @brief Get a set of lines exposed by this chip by their names.
+ * @param names Vector of line names.
+ * @return Set of lines held by a line_bulk object.
+ */
+ GPIOD_API line_bulk find_lines(const ::std::vector<::std::string>& names) const;
+
+ /**
+ * @brief Equality operator.
+ * @param rhs Right-hand side of the equation.
+ * @return True if rhs references the same chip. False otherwise.
+ */
+ GPIOD_API bool operator==(const chip& rhs) const noexcept;
+
+ /**
+ * @brief Inequality operator.
+ * @param rhs Right-hand side of the equation.
+ * @return False if rhs references the same chip. True otherwise.
+ */
+ GPIOD_API bool operator!=(const chip& rhs) const noexcept;
+
+ /**
+ * @brief Check if this object holds a reference to a GPIO chip.
+ * @return True if this object references a GPIO chip, false otherwise.
+ */
+ GPIOD_API operator bool(void) const noexcept;
+
+ /**
+ * @brief Check if this object doesn't hold a reference to a GPIO chip.
+ * @return False if this object references a GPIO chip, true otherwise.
+ */
+ GPIOD_API bool operator!(void) const noexcept;
+
+ /**
+ * @brief Affect the way in which chip::chip and chip::open will try
+ * to open a GPIO chip character device.
+ */
+ enum : int {
+ OPEN_LOOKUP = 1,
+ /**< Open based on the best guess what the supplied string is. */
+ OPEN_BY_PATH,
+ /**< Assume the string is a path to the GPIO chardev. */
+ OPEN_BY_NAME,
+ /**< Assume the string is the name of the chip */
+ OPEN_BY_LABEL,
+ /**< Assume the string is the label of the GPIO chip. */
+ OPEN_BY_NUMBER,
+ /**< Assume the string is the number of the GPIO chip. */
+ };
+
+private:
+
+ chip(::gpiod_chip* chip);
+
+ void throw_if_noref(void) const;
+
+ ::std::shared_ptr<::gpiod_chip> _m_chip;
+
+ friend chip_iterator;
+ friend line_iterator;
+};
+
+/**
+ * @brief Stores the configuration for line requests.
+ */
+struct line_request
+{
+ /**
+ * @brief Request types.
+ */
+ enum : int {
+ DIRECTION_AS_IS = 1,
+ /**< Request for values, don't change the direction. */
+ DIRECTION_INPUT,
+ /**< Request for reading line values. */
+ DIRECTION_OUTPUT,
+ /**< Request for driving the GPIO lines. */
+ EVENT_FALLING_EDGE,
+ /**< Listen for falling edge events. */
+ EVENT_RISING_EDGE,
+ /**< Listen for rising edge events. */
+ EVENT_BOTH_EDGES,
+ /**< Listen for all types of events. */
+ };
+
+ static const ::std::bitset<32> FLAG_ACTIVE_LOW;
+ /**< Set the active state to 'low' (high is the default). */
+ static const ::std::bitset<32> FLAG_OPEN_SOURCE;
+ /**< The line is an open-source port. */
+ static const ::std::bitset<32> FLAG_OPEN_DRAIN;
+ /**< The line is an open-drain port. */
+
+ ::std::string consumer;
+ /**< Consumer name to pass to the request. */
+ int request_type;
+ /**< Type of the request. */
+ ::std::bitset<32> flags;
+ /**< Additional request flags. */
+};
+
+/**
+ * @brief Represents a single GPIO line.
+ *
+ * Internally this class holds a raw pointer to a GPIO line descriptor and a
+ * reference to the parent chip. All line resources are freed when the last
+ * reference to the parent chip is dropped.
+ */
+class line
+{
+public:
+
+ /**
+ * @brief Default constructor. Creates an empty line object.
+ */
+ GPIOD_API line(void);
+
+ /**
+ * @brief Copy constructor.
+ * @param other Other line object.
+ */
+ GPIOD_API line(const line& other) = default;
+
+ /**
+ * @brief Move constructor.
+ * @param other Other line object.
+ */
+ GPIOD_API line(line&& other) = default;
+
+ /**
+ * @brief Assignment operator.
+ * @param other Other line object.
+ * @return Reference to this object.
+ */
+ GPIOD_API line& operator=(const line& other) = default;
+
+ /**
+ * @brief Move assignment operator.
+ * @param other Other line object.
+ * @return Reference to this object.
+ */
+ GPIOD_API line& operator=(line&& other) = default;
+
+ /**
+ * @brief Destructor.
+ */
+ GPIOD_API ~line(void) = default;
+
+ /**
+ * @brief Get the offset of this line.
+ * @return Offet of this line.
+ */
+ GPIOD_API unsigned int offset(void) const;
+
+ /**
+ * @brief Get the name of this line (if any).
+ * @return Name of this line or an empty string if it is unnamed.
+ */
+ GPIOD_API ::std::string name(void) const;
+
+ /**
+ * @brief Get the consumer of this line (if any).
+ * @return Name of the consumer of this line or an empty string if it
+ * is unused.
+ */
+ GPIOD_API ::std::string consumer(void) const;
+
+ /**
+ * @brief Get current direction of this line.
+ * @return Current direction setting.
+ */
+ GPIOD_API int direction(void) const noexcept;
+
+ /**
+ * @brief Get current active state of this line.
+ * @return Current active state setting.
+ */
+ GPIOD_API int active_state(void) const noexcept;
+
+ /**
+ * @brief Check if this line is used by the kernel or other user space
+ * process.
+ * @return True if this line is in use, false otherwise.
+ */
+ GPIOD_API bool is_used(void) const;
+
+ /**
+ * @brief Check if this line represents an open-drain GPIO.
+ * @return True if the line is an open-drain GPIO, false otherwise.
+ */
+ GPIOD_API bool is_open_drain(void) const;
+
+ /**
+ * @brief Check if this line represents an open-source GPIO.
+ * @return True if the line is an open-source GPIO, false otherwise.
+ */
+ GPIOD_API bool is_open_source(void) const;
+
+ /**
+ * @brief Request this line.
+ * @param config Request config (see gpiod::line_request).
+ * @param default_val Default value - only matters for OUTPUT direction.
+ */
+ GPIOD_API void request(const line_request& config, int default_val = 0) const;
+
+ /**
+ * @brief Check if this user has ownership of this line.
+ * @return True if the user has ownership of this line, false otherwise.
+ */
+ GPIOD_API bool is_requested(void) const;
+
+ /**
+ * @brief Read the line value.
+ * @return Current value (0 or 1).
+ */
+ GPIOD_API int get_value(void) const;
+
+ /**
+ * @brief Set the value of this line.
+ * @param val New value (0 or 1).
+ */
+ GPIOD_API void set_value(int val) const;
+
+ /**
+ * @brief Wait for an event on this line.
+ * @param timeout Time to wait before returning if no event occurred.
+ * @return True if an event occurred and can be read, false if the wait
+ * timed out.
+ */
+ GPIOD_API bool event_wait(const ::std::chrono::nanoseconds& timeout) const;
+
+ /**
+ * @brief Read a line event.
+ * @return Line event object.
+ */
+ GPIOD_API line_event event_read(void) const;
+
+ /**
+ * @brief Get the event file descriptor associated with this line.
+ * @return File descriptor number.
+ */
+ GPIOD_API int event_get_fd(void) const;
+
+ /**
+ * @brief Get the reference to the parent chip.
+ * @return Reference to the parent chip object.
+ */
+ GPIOD_API const chip& get_chip(void) const;
+
+ /**
+ * @brief Reset the state of this object.
+ *
+ * This is useful when the user needs to e.g. keep the line_event object
+ * but wants to drop the reference to the GPIO chip indirectly held by
+ * the line being the source of the event.
+ */
+ GPIOD_API void reset(void);
+
+ /**
+ * @brief Check if two line objects reference the same GPIO line.
+ * @param rhs Right-hand side of the equation.
+ * @return True if both objects reference the same line, fale otherwise.
+ */
+ GPIOD_API bool operator==(const line& rhs) const noexcept;
+
+ /**
+ * @brief Check if two line objects reference different GPIO lines.
+ * @param rhs Right-hand side of the equation.
+ * @return False if both objects reference the same line, true otherwise.
+ */
+ GPIOD_API bool operator!=(const line& rhs) const noexcept;
+
+ /**
+ * @brief Check if this object holds a reference to any GPIO line.
+ * @return True if this object references a GPIO line, false otherwise.
+ */
+ GPIOD_API operator bool(void) const noexcept;
+
+ /**
+ * @brief Check if this object doesn't reference any GPIO line.
+ * @return True if this object doesn't reference any GPIO line, true
+ * otherwise.
+ */
+ GPIOD_API bool operator!(void) const noexcept;
+
+ /**
+ * @brief Possible direction settings.
+ */
+ enum : int {
+ DIRECTION_INPUT = 1,
+ /**< Line's direction setting is input. */
+ DIRECTION_OUTPUT,
+ /**< Line's direction setting is output. */
+ };
+
+ /**
+ * @brief Possible active state settings.
+ */
+ enum : int {
+ ACTIVE_LOW = 1,
+ /**< Line's active state is low. */
+ ACTIVE_HIGH,
+ /**< Line's active state is high. */
+ };
+
+private:
+
+ line(::gpiod_line* line, const chip& owner);
+
+ void throw_if_null(void) const;
+
+ ::gpiod_line* _m_line;
+ chip _m_chip;
+
+ friend chip;
+ friend line_bulk;
+ friend line_iterator;
+};
+
+/**
+ * @brief Find a GPIO line by name. Search all GPIO chips present on the system.
+ * @param name Name of the line.
+ * @return Returns a line object - empty if the line was not found.
+ */
+GPIOD_API line find_line(const ::std::string& name);
+
+/**
+ * @brief Describes a single GPIO line event.
+ */
+struct line_event
+{
+
+ /**
+ * @brief Possible event types.
+ */
+ enum : int {
+ RISING_EDGE = 1,
+ /**< Rising edge event. */
+ FALLING_EDGE,
+ /**< Falling edge event. */
+ };
+
+ ::std::chrono::nanoseconds timestamp;
+ /**< Best estimate of time of event occurrence in nanoseconds. */
+ int event_type;
+ /**< Type of the event that occurred. */
+ line source;
+ /**< Line object referencing the GPIO line on which the event occurred. */
+};
+
+/**
+ * @brief Represents a set of GPIO lines.
+ *
+ * Internally an object of this class stores an array of line objects
+ * owned by a single chip.
+ */
+class line_bulk
+{
+public:
+
+ /**
+ * @brief Default constructor. Creates an empty line_bulk object.
+ */
+ GPIOD_API line_bulk(void) = default;
+
+ /**
+ * @brief Construct a line_bulk from a vector of lines.
+ * @param lines Vector of gpiod::line objects.
+ * @note All lines must be owned by the same GPIO chip.
+ */
+ GPIOD_API line_bulk(const ::std::vector<line>& lines);
+
+ /**
+ * @brief Copy constructor.
+ * @param other Other line_bulk object.
+ */
+ GPIOD_API line_bulk(const line_bulk& other) = default;
+
+ /**
+ * @brief Move constructor.
+ * @param other Other line_bulk object.
+ */
+ GPIOD_API line_bulk(line_bulk&& other) = default;
+
+ /**
+ * @brief Assignment operator.
+ * @param other Other line_bulk object.
+ * @return Reference to this object.
+ */
+ GPIOD_API line_bulk& operator=(const line_bulk& other) = default;
+
+ /**
+ * @brief Move assignment operator.
+ * @param other Other line_bulk object.
+ * @return Reference to this object.
+ */
+ GPIOD_API line_bulk& operator=(line_bulk&& other) = default;
+
+ /**
+ * @brief Destructor.
+ */
+ GPIOD_API ~line_bulk(void) = default;
+
+ /**
+ * @brief Add a line to this line_bulk object.
+ * @param new_line Line to add.
+ * @note The new line must be owned by the same chip as all the other
+ * lines already held by this line_bulk object.
+ */
+ GPIOD_API void add(const line& new_line);
+
+ /**
+ * @brief Get the line at given offset.
+ * @param offset Offset of the line to get.
+ * @return Reference to the line object.
+ */
+ GPIOD_API line& get(unsigned int offset);
+
+ /**
+ * @brief Get the line at given offset without bounds checking.
+ * @param offset Offset of the line to get.
+ * @return Reference to the line object.
+ * @note No bounds checking is performed.
+ */
+ GPIOD_API line& operator[](unsigned int offset);
+
+ /**
+ * @brief Get the number of lines currently held by this object.
+ * @return Number of elements in this line_bulk.
+ */
+ GPIOD_API unsigned int size(void) const noexcept;
+
+ /**
+ * @brief Check if this line_bulk doesn't hold any lines.
+ * @return True if this object is empty, false otherwise.
+ */
+ GPIOD_API bool empty(void) const noexcept;
+
+ /**
+ * @brief Remove all lines from this object.
+ */
+ GPIOD_API void clear(void);
+
+ /**
+ * @brief Request all lines held by this object.
+ * @param config Request config (see gpiod::line_request).
+ * @param default_vals Vector of default values. Only relevant for
+ * output direction requests.
+ */
+ GPIOD_API void request(const line_request& config,
+ std::vector<int> default_vals = std::vector<int>()) const;
+
+ /**
+ * @brief Read values from all lines held by this object.
+ * @return Vector containing line values the order of which corresponds
+ * with the order of lines in the internal array.
+ */
+ GPIOD_API ::std::vector<int> get_values(void) const;
+
+ /**
+ * @brief Set values of all lines held by this object.
+ * @param values Vector of values to set. Must be the same size as the
+ * number of lines held by this line_bulk.
+ */
+ GPIOD_API void set_values(const ::std::vector<int>& values) const;
+
+ /**
+ * @brief Poll the set of lines for line events.
+ * @param timeout Number of nanoseconds to wait before returning an
+ * empty line_bulk.
+ * @return Returns a line_bulk object containing lines on which events
+ * occurred.
+ */
+ GPIOD_API line_bulk event_wait(const ::std::chrono::nanoseconds& timeout) const;
+
+ /**
+ * @brief Check if this object holds any lines.
+ * @return True if this line_bulk holds at least one line, false otherwise.
+ */
+ GPIOD_API operator bool(void) const noexcept;
+
+ /**
+ * @brief Check if this object doesn't hold any lines.
+ * @return True if this line_bulk is empty, false otherwise.
+ */
+ GPIOD_API bool operator!(void) const noexcept;
+
+ /**
+ * @brief Max number of lines that this object can hold.
+ */
+ GPIOD_API static const unsigned int MAX_LINES;
+
+ /**
+ * @brief Iterator for iterating over lines held by line_bulk.
+ */
+ class iterator
+ {
+ public:
+
+ /**
+ * @brief Default constructor. Builds an empty iterator object.
+ */
+ GPIOD_API iterator(void) = default;
+
+ /**
+ * @brief Copy constructor.
+ * @param other Other line_bulk iterator.
+ */
+ GPIOD_API iterator(const iterator& other) = default;
+
+ /**
+ * @brief Move constructor.
+ * @param other Other line_bulk iterator.
+ */
+ GPIOD_API iterator(iterator&& other) = default;
+
+ /**
+ * @brief Assignment operator.
+ * @param other Other line_bulk iterator.
+ * @return Reference to this iterator.
+ */
+ GPIOD_API iterator& operator=(const iterator& other) = default;
+
+ /**
+ * @brief Move assignment operator.
+ * @param other Other line_bulk iterator.
+ * @return Reference to this iterator.
+ */
+ GPIOD_API iterator& operator=(iterator&& other) = default;
+
+ /**
+ * @brief Destructor.
+ */
+ GPIOD_API ~iterator(void) = default;
+
+ /**
+ * @brief Advance the iterator by one element.
+ * @return Reference to this iterator.
+ */
+ GPIOD_API iterator& operator++(void);
+
+ /**
+ * @brief Dereference current element.
+ * @return Current GPIO line by reference.
+ */
+ GPIOD_API const line& operator*(void) const;
+
+ /**
+ * @brief Member access operator.
+ * @return Current GPIO line by pointer.
+ */
+ GPIOD_API const line* operator->(void) const;
+
+ /**
+ * @brief Check if this operator points to the same element.
+ * @param rhs Right-hand side of the equation.
+ * @return True if this iterator points to the same GPIO line,
+ * false otherwise.
+ */
+ GPIOD_API bool operator==(const iterator& rhs) const noexcept;
+
+ /**
+ * @brief Check if this operator doesn't point to the same element.
+ * @param rhs Right-hand side of the equation.
+ * @return True if this iterator doesn't point to the same GPIO
+ * line, false otherwise.
+ */
+ GPIOD_API bool operator!=(const iterator& rhs) const noexcept;
+
+ private:
+
+ iterator(const ::std::vector<line>::iterator& it);
+
+ ::std::vector<line>::iterator _m_iter;
+
+ friend line_bulk;
+ };
+
+ /**
+ * @brief Returns an iterator to the first line.
+ * @return A line_bulk iterator.
+ */
+ GPIOD_API iterator begin(void) noexcept;
+
+ /**
+ * @brief Returns an iterator to the element following the last line.
+ * @return A line_bulk iterator.
+ */
+ GPIOD_API iterator end(void) noexcept;
+
+private:
+
+ void throw_if_empty(void) const;
+ void to_line_bulk(::gpiod_line_bulk* bulk) const;
+
+ ::std::vector<line> _m_bulk;
+};
+
+/**
+ * @brief Create a new chip_iterator.
+ * @return New chip iterator object pointing to the first GPIO chip on the system.
+ * @note This function is needed as we already use the default constructor of
+ * gpiod::chip_iterator as the return value of gpiod::end.
+ */
+GPIOD_API chip_iterator make_chip_iterator(void);
+
+/**
+ * @brief Support for range-based loops for chip iterators.
+ * @param iter A chip iterator.
+ * @return Iterator unchanged.
+ */
+GPIOD_API chip_iterator begin(chip_iterator iter) noexcept;
+
+/**
+ * @brief Support for range-based loops for chip iterators.
+ * @param iter A chip iterator.
+ * @return New end iterator.
+ */
+GPIOD_API chip_iterator end(const chip_iterator& iter) noexcept;
+
+/**
+ * @brief Allows to iterate over all GPIO chips present on the system.
+ */
+class chip_iterator
+{
+public:
+
+ /**
+ * @brief Default constructor. Creates the end iterator.
+ */
+ GPIOD_API chip_iterator(void) = default;
+
+ /**
+ * @brief Copy constructor.
+ * @param other Other chip_iterator.
+ */
+ GPIOD_API chip_iterator(const chip_iterator& other) = default;
+
+ /**
+ * @brief Move constructor.
+ * @param other Other chip_iterator.
+ */
+ GPIOD_API chip_iterator(chip_iterator&& other) = default;
+
+ /**
+ * @brief Assignment operator.
+ * @param other Other chip_iterator.
+ * @return Reference to this iterator.
+ */
+ GPIOD_API chip_iterator& operator=(const chip_iterator& other) = default;
+
+ /**
+ * @brief Move assignment operator.
+ * @param other Other chip_iterator.
+ * @return Reference to this iterator.
+ */
+ GPIOD_API chip_iterator& operator=(chip_iterator&& other) = default;
+
+ /**
+ * @brief Destructor.
+ */
+ GPIOD_API ~chip_iterator(void) = default;
+
+ /**
+ * @brief Advance the iterator by one element.
+ * @return Reference to this iterator.
+ */
+ GPIOD_API chip_iterator& operator++(void);
+
+ /**
+ * @brief Dereference current element.
+ * @return Current GPIO chip by reference.
+ */
+ GPIOD_API const chip& operator*(void) const;
+
+ /**
+ * @brief Member access operator.
+ * @return Current GPIO chip by pointer.
+ */
+ GPIOD_API const chip* operator->(void) const;
+
+ /**
+ * @brief Check if this operator points to the same element.
+ * @param rhs Right-hand side of the equation.
+ * @return True if this iterator points to the same chip_iterator,
+ * false otherwise.
+ */
+ GPIOD_API bool operator==(const chip_iterator& rhs) const noexcept;
+
+ /**
+ * @brief Check if this operator doesn't point to the same element.
+ * @param rhs Right-hand side of the equation.
+ * @return True if this iterator doesn't point to the same chip_iterator,
+ * false otherwise.
+ */
+ GPIOD_API bool operator!=(const chip_iterator& rhs) const noexcept;
+
+private:
+
+ chip_iterator(::gpiod_chip_iter* iter);
+
+ ::std::shared_ptr<::gpiod_chip_iter> _m_iter;
+ chip _m_current;
+
+ friend chip_iterator make_chip_iterator(void);
+};
+
+/**
+ * @brief Support for range-based loops for line iterators.
+ * @param iter A line iterator.
+ * @return Iterator unchanged.
+ */
+GPIOD_API line_iterator begin(line_iterator iter) noexcept;
+
+/**
+ * @brief Support for range-based loops for line iterators.
+ * @param iter A line iterator.
+ * @return New end iterator.
+ */
+GPIOD_API line_iterator end(const line_iterator& iter) noexcept;
+
+/**
+ * @brief Allows to iterate over all lines owned by a GPIO chip.
+ */
+class line_iterator
+{
+public:
+
+ /**
+ * @brief Default constructor. Creates the end iterator.
+ */
+ GPIOD_API line_iterator(void) = default;
+
+ /**
+ * @brief Constructor. Creates the begin iterator.
+ * @param owner Chip owning the GPIO lines over which we want to iterate.
+ */
+ GPIOD_API line_iterator(const chip& owner);
+
+ /**
+ * @brief Copy constructor.
+ * @param other Other line iterator.
+ */
+ GPIOD_API line_iterator(const line_iterator& other) = default;
+
+ /**
+ * @brief Move constructor.
+ * @param other Other line iterator.
+ */
+ GPIOD_API line_iterator(line_iterator&& other) = default;
+
+ /**
+ * @brief Assignment operator.
+ * @param other Other line iterator.
+ * @return Reference to this line_iterator.
+ */
+ GPIOD_API line_iterator& operator=(const line_iterator& other) = default;
+
+ /**
+ * @brief Move assignment operator.
+ * @param other Other line iterator.
+ * @return Reference to this line_iterator.
+ */
+ GPIOD_API line_iterator& operator=(line_iterator&& other) = default;
+
+ /**
+ * @brief Destructor.
+ */
+ GPIOD_API ~line_iterator(void) = default;
+
+ /**
+ * @brief Advance the iterator by one element.
+ * @return Reference to this iterator.
+ */
+ GPIOD_API line_iterator& operator++(void);
+
+ /**
+ * @brief Dereference current element.
+ * @return Current GPIO line by reference.
+ */
+ GPIOD_API const line& operator*(void) const;
+
+ /**
+ * @brief Member access operator.
+ * @return Current GPIO line by pointer.
+ */
+ GPIOD_API const line* operator->(void) const;
+
+ /**
+ * @brief Check if this operator points to the same element.
+ * @param rhs Right-hand side of the equation.
+ * @return True if this iterator points to the same line_iterator,
+ * false otherwise.
+ */
+ GPIOD_API bool operator==(const line_iterator& rhs) const noexcept;
+
+ /**
+ * @brief Check if this operator doesn't point to the same element.
+ * @param rhs Right-hand side of the equation.
+ * @return True if this iterator doesn't point to the same line_iterator,
+ * false otherwise.
+ */
+ GPIOD_API bool operator!=(const line_iterator& rhs) const noexcept;
+
+private:
+
+ ::std::shared_ptr<::gpiod_line_iter> _m_iter;
+ line _m_current;
+};
+
+/**
+ * @}
+ */
+
+} /* namespace gpiod */
+
+#endif /* __LIBGPIOD_GPIOD_CXX_HPP__ */
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <gpiod.hpp>
+
+#include <system_error>
+
+namespace gpiod {
+
+namespace {
+
+void chip_iter_deleter(::gpiod_chip_iter* iter)
+{
+ ::gpiod_chip_iter_free_noclose(iter);
+}
+
+void line_iter_deleter(::gpiod_line_iter* iter)
+{
+ ::gpiod_line_iter_free(iter);
+}
+
+::gpiod_line_iter* make_line_iterator(::gpiod_chip* chip)
+{
+ ::gpiod_line_iter* iter;
+
+ iter = ::gpiod_line_iter_new(chip);
+ if (!iter)
+ throw ::std::system_error(errno, ::std::system_category(),
+ "error creating GPIO line iterator");
+
+ return iter;
+}
+
+} /* namespace */
+
+chip_iterator make_chip_iterator(void)
+{
+ ::gpiod_chip_iter* iter = ::gpiod_chip_iter_new();
+ if (!iter)
+ throw ::std::system_error(errno, ::std::system_category(),
+ "error creating GPIO chip iterator");
+
+ return ::std::move(chip_iterator(iter));
+}
+
+bool chip_iterator::operator==(const chip_iterator& rhs) const noexcept
+{
+ return this->_m_current == rhs._m_current;
+}
+
+bool chip_iterator::operator!=(const chip_iterator& rhs) const noexcept
+{
+ return this->_m_current != rhs._m_current;
+}
+
+chip_iterator::chip_iterator(::gpiod_chip_iter *iter)
+ : _m_iter(iter, chip_iter_deleter),
+ _m_current(chip(::gpiod_chip_iter_next_noclose(this->_m_iter.get())))
+{
+
+}
+
+chip_iterator& chip_iterator::operator++(void)
+{
+ ::gpiod_chip* next = ::gpiod_chip_iter_next_noclose(this->_m_iter.get());
+
+ this->_m_current = next ? chip(next) : chip();
+
+ return *this;
+}
+
+const chip& chip_iterator::operator*(void) const
+{
+ return this->_m_current;
+}
+
+const chip* chip_iterator::operator->(void) const
+{
+ return ::std::addressof(this->_m_current);
+}
+
+chip_iterator begin(chip_iterator iter) noexcept
+{
+ return iter;
+}
+
+chip_iterator end(const chip_iterator&) noexcept
+{
+ return ::std::move(chip_iterator());
+}
+
+line_iterator begin(line_iterator iter) noexcept
+{
+ return iter;
+}
+
+line_iterator end(const line_iterator&) noexcept
+{
+ return ::std::move(line_iterator());
+}
+
+line_iterator::line_iterator(const chip& owner)
+ : _m_iter(make_line_iterator(owner._m_chip.get()), line_iter_deleter),
+ _m_current(line(::gpiod_line_iter_next(this->_m_iter.get()), owner))
+{
+
+}
+
+line_iterator& line_iterator::operator++(void)
+{
+ ::gpiod_line* next = ::gpiod_line_iter_next(this->_m_iter.get());
+
+ this->_m_current = next ? line(next, this->_m_current._m_chip) : line();
+
+ return *this;
+}
+
+const line& line_iterator::operator*(void) const
+{
+ return this->_m_current;
+}
+
+const line* line_iterator::operator->(void) const
+{
+ return ::std::addressof(this->_m_current);
+}
+
+bool line_iterator::operator==(const line_iterator& rhs) const noexcept
+{
+ return this->_m_current._m_line == rhs._m_current._m_line;
+}
+
+bool line_iterator::operator!=(const line_iterator& rhs) const noexcept
+{
+ return this->_m_current._m_line != rhs._m_current._m_line;
+}
+
+} /* namespace gpiod */
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <gpiod.hpp>
+
+#include <system_error>
+
+namespace gpiod {
+
+line::line(void)
+ : _m_line(nullptr),
+ _m_chip()
+{
+
+}
+
+line::line(::gpiod_line* line, const chip& owner)
+ : _m_line(line),
+ _m_chip(owner)
+{
+
+}
+
+unsigned int line::offset(void) const
+{
+ this->throw_if_null();
+
+ return ::gpiod_line_offset(this->_m_line);
+}
+
+::std::string line::name(void) const
+{
+ this->throw_if_null();
+
+ const char* name = ::gpiod_line_name(this->_m_line);
+
+ return ::std::move(name ? ::std::string(name) : ::std::string());
+}
+
+::std::string line::consumer(void) const
+{
+ this->throw_if_null();
+
+ const char* consumer = ::gpiod_line_consumer(this->_m_line);
+
+ return ::std::move(consumer ? ::std::string(consumer) : ::std::string());
+}
+
+int line::direction(void) const noexcept
+{
+ this->throw_if_null();
+
+ int dir = ::gpiod_line_direction(this->_m_line);
+
+ return dir == GPIOD_LINE_DIRECTION_INPUT ? DIRECTION_INPUT : DIRECTION_OUTPUT;
+}
+
+int line::active_state(void) const noexcept
+{
+ this->throw_if_null();
+
+ int active = ::gpiod_line_active_state(this->_m_line);
+
+ return active == GPIOD_LINE_ACTIVE_STATE_HIGH ? ACTIVE_HIGH : ACTIVE_LOW;
+}
+
+bool line::is_used(void) const
+{
+ this->throw_if_null();
+
+ return ::gpiod_line_is_used(this->_m_line);
+}
+
+bool line::is_open_drain(void) const
+{
+ this->throw_if_null();
+
+ return ::gpiod_line_is_open_drain(this->_m_line);
+}
+
+bool line::is_open_source(void) const
+{
+ this->throw_if_null();
+
+ return ::gpiod_line_is_open_source(this->_m_line);
+}
+
+void line::request(const line_request& config, int default_val) const
+{
+ this->throw_if_null();
+
+ line_bulk bulk({ *this });
+
+ bulk.request(config, { default_val });
+}
+
+bool line::is_requested(void) const
+{
+ this->throw_if_null();
+
+ return ::gpiod_line_is_requested(this->_m_line);
+}
+
+/*
+ * REVISIT: Check the performance of get/set_value & event_wait compared to
+ * the C API. Creating a line_bulk object involves a memory allocation every
+ * time this method if called. If the performance is significantly lower,
+ * switch to calling the C functions for setting/getting line values and
+ * polling for events on single lines directly.
+ */
+
+int line::get_value(void) const
+{
+ this->throw_if_null();
+
+ line_bulk bulk({ *this });
+
+ return bulk.get_values()[0];
+}
+
+void line::set_value(int val) const
+{
+ this->throw_if_null();
+
+ line_bulk bulk({ *this });
+
+ bulk.set_values({ val });
+}
+
+bool line::event_wait(const ::std::chrono::nanoseconds& timeout) const
+{
+ this->throw_if_null();
+
+ line_bulk bulk({ *this });
+
+ line_bulk event_bulk = bulk.event_wait(timeout);
+
+ return ::std::move(event_bulk);
+}
+
+line_event line::event_read(void) const
+{
+ this->throw_if_null();
+
+ ::gpiod_line_event event_buf;
+ line_event event;
+ int rv;
+
+ rv = ::gpiod_line_event_read(this->_m_line, ::std::addressof(event_buf));
+ if (rv < 0)
+ throw ::std::system_error(errno, ::std::system_category(),
+ "error reading line event");
+
+ if (event_buf.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
+ event.event_type = line_event::RISING_EDGE;
+ else if (event_buf.event_type == GPIOD_LINE_EVENT_FALLING_EDGE)
+ event.event_type = line_event::FALLING_EDGE;
+
+ event.timestamp = ::std::chrono::nanoseconds(
+ event_buf.ts.tv_nsec + (event_buf.ts.tv_sec * 1000000000));
+
+ event.source = *this;
+
+ return ::std::move(event);
+}
+
+int line::event_get_fd(void) const
+{
+ this->throw_if_null();
+
+ int ret = ::gpiod_line_event_get_fd(this->_m_line);
+
+ if (ret < 0)
+ ::std::system_error(errno, ::std::system_category(),
+ "unable to get the line event file descriptor");
+
+ return ret;
+}
+
+const chip& line::get_chip(void) const
+{
+ return this->_m_chip;
+}
+
+void line::reset(void)
+{
+ this->_m_line = nullptr;
+ this->_m_chip.reset();
+}
+
+bool line::operator==(const line& rhs) const noexcept
+{
+ return this->_m_line == rhs._m_line;
+}
+
+bool line::operator!=(const line& rhs) const noexcept
+{
+ return this->_m_line != rhs._m_line;
+}
+
+line::operator bool(void) const noexcept
+{
+ return this->_m_line != nullptr;
+}
+
+bool line::operator!(void) const noexcept
+{
+ return this->_m_line == nullptr;
+}
+
+void line::throw_if_null(void) const
+{
+ if (!this->_m_line)
+ throw ::std::logic_error("object not holding a GPIO line handle");
+}
+
+line find_line(const ::std::string& name)
+{
+ line ret;
+
+ for (auto& it: make_chip_iterator()) {
+ ret = it.find_line(name);
+ if (ret)
+ break;
+ }
+
+ return ::std::move(ret);
+}
+
+} /* namespace gpiod */
--- /dev/null
+/*
+ * This file is part of libgpiod.
+ *
+ * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <gpiod.hpp>
+
+#include <system_error>
+#include <map>
+
+namespace gpiod {
+
+namespace {
+
+const ::std::map<int, int> reqtype_mapping = {
+ { line_request::DIRECTION_AS_IS, GPIOD_LINE_REQUEST_DIRECTION_AS_IS, },
+ { line_request::DIRECTION_INPUT, GPIOD_LINE_REQUEST_DIRECTION_INPUT, },
+ { line_request::DIRECTION_OUTPUT, GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, },
+ { line_request::EVENT_FALLING_EDGE, GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE, },
+ { line_request::EVENT_RISING_EDGE, GPIOD_LINE_REQUEST_EVENT_RISING_EDGE, },
+ { line_request::EVENT_BOTH_EDGES, GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES, },
+};
+
+struct bitset_cmp
+{
+ bool operator()(const ::std::bitset<32>& lhs, const ::std::bitset<32>& rhs)
+ {
+ return lhs.to_ulong() < rhs.to_ulong();
+ }
+};
+
+const ::std::map<::std::bitset<32>, int, bitset_cmp> reqflag_mapping = {
+ { line_request::FLAG_ACTIVE_LOW, GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW, },
+ { line_request::FLAG_OPEN_DRAIN, GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN, },
+ { line_request::FLAG_OPEN_SOURCE, GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE, },
+};
+
+} /* namespace */
+
+const ::std::bitset<32> line_request::FLAG_ACTIVE_LOW("001");
+const ::std::bitset<32> line_request::FLAG_OPEN_SOURCE("010");
+const ::std::bitset<32> line_request::FLAG_OPEN_DRAIN("100");
+
+const unsigned int line_bulk::MAX_LINES = GPIOD_LINE_BULK_MAX_LINES;
+
+line_bulk::line_bulk(const ::std::vector<line>& lines)
+ : _m_bulk()
+{
+ this->_m_bulk.reserve(lines.size());
+
+ for (auto& it: lines)
+ this->add(it);
+}
+
+void line_bulk::add(const line& new_line)
+{
+ if (!new_line)
+ throw ::std::logic_error("line_bulk cannot hold empty line objects");
+
+ if (this->_m_bulk.size() >= MAX_LINES)
+ throw ::std::logic_error("maximum number of lines reached");
+
+ if (this->_m_bulk.size() >= 1 && this->_m_bulk.begin()->get_chip() != new_line.get_chip())
+ throw std::logic_error("line_bulk cannot hold GPIO lines from different chips");
+
+ this->_m_bulk.push_back(new_line);
+}
+
+line& line_bulk::get(unsigned int offset)
+{
+ return this->_m_bulk.at(offset);
+}
+
+line& line_bulk::operator[](unsigned int offset)
+{
+ return this->_m_bulk[offset];
+}
+
+unsigned int line_bulk::size(void) const noexcept
+{
+ return this->_m_bulk.size();
+}
+
+bool line_bulk::empty(void) const noexcept
+{
+ return this->_m_bulk.empty();
+}
+
+void line_bulk::clear(void)
+{
+ this->_m_bulk.clear();
+}
+
+void line_bulk::request(const line_request& config, std::vector<int> default_vals) const
+{
+ this->throw_if_empty();
+
+ if (!default_vals.empty() && this->size() != default_vals.size())
+ throw ::std::invalid_argument("the number of default values must correspond with the number of lines");
+
+ ::gpiod_line_request_config conf;
+ ::gpiod_line_bulk bulk;
+ int rv;
+
+ this->to_line_bulk(::std::addressof(bulk));
+
+ conf.consumer = config.consumer.c_str();
+ conf.request_type = reqtype_mapping.at(config.request_type);
+ conf.flags = 0;
+
+ for (auto& it: reqflag_mapping) {
+ if ((it.first & config.flags).to_ulong())
+ conf.flags |= it.second;
+ }
+
+ rv = ::gpiod_line_request_bulk(::std::addressof(bulk),
+ ::std::addressof(conf), default_vals.data());
+ if (rv)
+ throw ::std::system_error(errno, ::std::system_category(),
+ "error requesting GPIO lines");
+}
+
+::std::vector<int> line_bulk::get_values(void) const
+{
+ this->throw_if_empty();
+
+ ::std::vector<int> values;
+ ::gpiod_line_bulk bulk;
+ int rv;
+
+ this->to_line_bulk(::std::addressof(bulk));
+ values.resize(this->_m_bulk.size());
+
+ rv = ::gpiod_line_get_value_bulk(::std::addressof(bulk), values.data());
+ if (rv)
+ throw ::std::system_error(errno, ::std::system_category(),
+ "error reading GPIO line values");
+
+ return ::std::move(values);
+}
+
+void line_bulk::set_values(const ::std::vector<int>& values) const
+{
+ this->throw_if_empty();
+
+ if (values.size() != this->_m_bulk.size())
+ throw ::std::invalid_argument("the size of values array must correspond with the number of lines");
+
+ ::gpiod_line_bulk bulk;
+ int rv;
+
+ this->to_line_bulk(::std::addressof(bulk));
+
+ rv = ::gpiod_line_set_value_bulk(::std::addressof(bulk), values.data());
+ if (rv)
+ throw ::std::system_error(errno, ::std::system_category(),
+ "error setting GPIO line values");
+}
+
+line_bulk line_bulk::event_wait(const ::std::chrono::nanoseconds& timeout) const
+{
+ this->throw_if_empty();
+
+ ::gpiod_line_bulk bulk, event_bulk;
+ ::timespec ts;
+ line_bulk ret;
+ int rv;
+
+ this->to_line_bulk(::std::addressof(bulk));
+
+ ::gpiod_line_bulk_init(::std::addressof(event_bulk));
+
+ ts.tv_sec = timeout.count() / 1000000000ULL;
+ ts.tv_nsec = timeout.count() % 1000000000ULL;
+
+ rv = ::gpiod_line_event_wait_bulk(::std::addressof(bulk),
+ ::std::addressof(ts),
+ ::std::addressof(event_bulk));
+ if (rv < 0) {
+ throw ::std::system_error(errno, ::std::system_category(),
+ "error polling for events");
+ } else if (rv > 0) {
+ for (unsigned int i = 0; i < event_bulk.num_lines; i++)
+ ret.add(line(event_bulk.lines[i], this->_m_bulk[i].get_chip()));
+ }
+
+ return ::std::move(ret);
+}
+
+line_bulk::operator bool(void) const noexcept
+{
+ return !this->_m_bulk.empty();
+}
+
+bool line_bulk::operator!(void) const noexcept
+{
+ return this->_m_bulk.empty();
+}
+
+line_bulk::iterator::iterator(const ::std::vector<line>::iterator& it)
+ : _m_iter(it)
+{
+
+}
+
+line_bulk::iterator& line_bulk::iterator::operator++(void)
+{
+ this->_m_iter++;
+
+ return *this;
+}
+
+const line& line_bulk::iterator::operator*(void) const
+{
+ return *this->_m_iter;
+}
+
+const line* line_bulk::iterator::operator->(void) const
+{
+ return this->_m_iter.operator->();
+}
+
+bool line_bulk::iterator::operator==(const iterator& rhs) const noexcept
+{
+ return this->_m_iter == rhs._m_iter;
+}
+
+bool line_bulk::iterator::operator!=(const iterator& rhs) const noexcept
+{
+ return this->_m_iter != rhs._m_iter;
+}
+
+line_bulk::iterator line_bulk::begin(void) noexcept
+{
+ return ::std::move(line_bulk::iterator(this->_m_bulk.begin()));
+}
+
+line_bulk::iterator line_bulk::end(void) noexcept
+{
+ return ::std::move(line_bulk::iterator(this->_m_bulk.end()));
+}
+
+void line_bulk::throw_if_empty(void) const
+{
+ if (this->_m_bulk.empty())
+ throw std::logic_error("line_bulk not holding any GPIO lines");
+}
+
+void line_bulk::to_line_bulk(::gpiod_line_bulk *bulk) const
+{
+ ::gpiod_line_bulk_init(bulk);
+ for (auto& it: this->_m_bulk)
+ ::gpiod_line_bulk_add(bulk, it._m_line);
+}
+
+} /* namespace gpiod */
AM_PROG_AR
AC_PROG_CC
+AC_PROG_CXX
AC_PROG_LIBTOOL
AC_PROG_INSTALL
PKG_CHECK_MODULES([UDEV], [libudev >= 215])
fi
+AC_ARG_ENABLE([bindings-cxx],
+ [AC_HELP_STRING([--enable-bindings-cxx],
+ [enable C++ bindings [default=no]])],
+ [
+ if test "x$enableval" = xyes
+ then
+ with_bindings_cxx=true
+ else
+ with_bindings_cxx=false
+ fi
+ ],
+ [with_bindings_cxx=false])
+AM_CONDITIONAL([WITH_BINDINGS_CXX], [test "x$with_bindings_cxx" = xtrue])
+
+if test "x$with_bindings_cxx" = xtrue
+then
+ AC_LIBTOOL_CXX
+ # This needs autoconf-archive
+ AX_CXX_COMPILE_STDCXX_11([ext], [mandatory])
+fi
+
AC_CHECK_PROG([has_doxygen], [doxygen], [true], [false])
AM_CONDITIONAL([HAS_DOXYGEN], [test "x$has_doxygen" = xtrue])
if test "x$has_doxygen" = xfalse
src/Makefile
src/lib/Makefile
src/tools/Makefile
- tests/Makefile])
+ tests/Makefile
+ bindings/Makefile
+ bindings/cxx/Makefile
+ bindings/cxx/examples/Makefile])
AC_OUTPUT