1
0
mirror of https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git synced 2026-10-25 12:39:18 +03:00

bindings: implement C++ bindings

Wrap the C API in a set of C++ classes which allow to use all libgpiod
functionalities in an object-oriented manner.

Include simplified reimplementations of gpio-tools and a set of
tests/examples showing how to use the C++ interface.

The C++ API is fully documented in doxygen.

Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com>
This commit is contained in:
Bartosz Golaszewski
2017-10-25 21:11:37 +02:00
parent a5a153d691
commit 8078a4a2ad
18 changed files with 2362 additions and 3 deletions

8
.gitignore vendored
View File

@@ -1,5 +1,6 @@
!.gitignore
libgpiod.la
libgpiodcxx.la
libtools-common.la
gpiodetect
gpioinfo
@@ -7,6 +8,13 @@ gpioget
gpioset
gpiomon
gpiofind
gpio_cxx_tests
gpiodetectcxx
gpiofindcxx
gpiogetcxx
gpioinfocxx
gpiomoncxx
gpiosetcxx
*.o
*.lo
doc

View File

@@ -9,7 +9,7 @@
ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = foreign
SUBDIRS = include src
SUBDIRS = include src bindings
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libgpiod.pc
@@ -23,7 +23,9 @@ endif
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

16
bindings/Makefile.am Normal file
View File

@@ -0,0 +1,16 @@
#
# 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

19
bindings/cxx/Makefile.am Normal file
View File

@@ -0,0 +1,19 @@
#
# 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

192
bindings/cxx/chip.cpp Normal file
View File

@@ -0,0 +1,192 @@
/*
* 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 */

View File

@@ -0,0 +1,34 @@
#
# 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

View File

@@ -0,0 +1,161 @@
/*
* 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;
}

View File

@@ -0,0 +1,33 @@
/*
* 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;
}

View File

@@ -0,0 +1,33 @@
/*
* 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;
}

View File

@@ -0,0 +1,47 @@
/*
* 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;
}

View File

@@ -0,0 +1,55 @@
/*
* 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;
}

View File

@@ -0,0 +1,73 @@
/*
* 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;
}

View File

@@ -0,0 +1,56 @@
/*
* 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;
}

959
bindings/cxx/gpiod.hpp Normal file
View File

@@ -0,0 +1,959 @@
/*
* 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__ */

146
bindings/cxx/iter.cpp Normal file
View File

@@ -0,0 +1,146 @@
/*
* 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 */

238
bindings/cxx/line.cpp Normal file
View File

@@ -0,0 +1,238 @@
/*
* 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 */

262
bindings/cxx/line_bulk.cpp Normal file
View File

@@ -0,0 +1,262 @@
/*
* 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 */

View File

@@ -33,6 +33,7 @@ AC_SUBST(AR_FLAGS, [cr])
AM_PROG_AR
AC_PROG_CC
AC_PROG_CXX
AC_PROG_LIBTOOL
AC_PROG_INSTALL
@@ -113,6 +114,27 @@ then
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
@@ -126,6 +148,9 @@ AC_CONFIG_FILES([libgpiod.pc
src/Makefile
src/lib/Makefile
src/tools/Makefile
tests/Makefile])
tests/Makefile
bindings/Makefile
bindings/cxx/Makefile
bindings/cxx/examples/Makefile])
AC_OUTPUT