| // SPDX-License-Identifier: LGPL-2.1-or-later |
| /* |
| * This file is part of libgpiod. |
| * |
| * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com> |
| */ |
| |
| #include <gpiod.hpp> |
| #include <array> |
| #include <map> |
| #include <system_error> |
| |
| namespace gpiod { |
| |
| namespace { |
| |
| const ::std::map<int, int> bias_mapping = { |
| { GPIOD_LINE_BIAS_PULL_UP, line::BIAS_PULL_UP, }, |
| { GPIOD_LINE_BIAS_PULL_DOWN, line::BIAS_PULL_DOWN, }, |
| { GPIOD_LINE_BIAS_DISABLE, line::BIAS_DISABLE, }, |
| { GPIOD_LINE_BIAS_AS_IS, line::BIAS_AS_IS, }, |
| }; |
| |
| } /* namespace */ |
| |
| 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 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 consumer ? ::std::string(consumer) : ::std::string(); |
| } |
| |
| int line::direction(void) const |
| { |
| 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 |
| { |
| this->throw_if_null(); |
| |
| int active = ::gpiod_line_active_state(this->_m_line); |
| |
| return active == GPIOD_LINE_ACTIVE_STATE_HIGH ? ACTIVE_HIGH : ACTIVE_LOW; |
| } |
| |
| int line::bias(void) const |
| { |
| this->throw_if_null(); |
| |
| return bias_mapping.at(::gpiod_line_bias(this->_m_line)); |
| } |
| |
| 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 }); |
| } |
| |
| void line::release(void) const |
| { |
| this->throw_if_null(); |
| |
| line_bulk bulk({ *this }); |
| |
| bulk.release(); |
| } |
| |
| 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 }); |
| } |
| |
| void line::set_config(int direction, ::std::bitset<32> flags, |
| int value) const |
| { |
| this->throw_if_null(); |
| |
| line_bulk bulk({ *this }); |
| |
| bulk.set_config(direction, flags, { value }); |
| } |
| |
| void line::set_flags(::std::bitset<32> flags) const |
| { |
| this->throw_if_null(); |
| |
| line_bulk bulk({ *this }); |
| |
| bulk.set_flags(flags); |
| } |
| |
| void line::set_direction_input() const |
| { |
| this->throw_if_null(); |
| |
| line_bulk bulk({ *this }); |
| |
| bulk.set_direction_input(); |
| } |
| |
| void line::set_direction_output(int value) const |
| { |
| this->throw_if_null(); |
| |
| line_bulk bulk({ *this }); |
| |
| bulk.set_direction_output({ value }); |
| } |
| |
| 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 !!event_bulk; |
| } |
| |
| line_event line::make_line_event(const ::gpiod_line_event& event) const noexcept |
| { |
| line_event ret; |
| |
| if (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE) |
| ret.event_type = line_event::RISING_EDGE; |
| else if (event.event_type == GPIOD_LINE_EVENT_FALLING_EDGE) |
| ret.event_type = line_event::FALLING_EDGE; |
| |
| ret.timestamp = ::std::chrono::duration_cast<::std::chrono::nanoseconds>( |
| ::std::chrono::seconds(event.ts.tv_sec)) + |
| ::std::chrono::nanoseconds(event.ts.tv_nsec); |
| |
| ret.source = *this; |
| |
| return ret; |
| } |
| |
| 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"); |
| |
| return this->make_line_event(event_buf); |
| } |
| |
| ::std::vector<line_event> line::event_read_multiple(void) const |
| { |
| this->throw_if_null(); |
| |
| /* 16 is the maximum number of events stored in the kernel FIFO. */ |
| ::std::array<::gpiod_line_event, 16> event_buf; |
| ::std::vector<line_event> events; |
| int rv; |
| |
| rv = ::gpiod_line_event_read_multiple(this->_m_line, |
| event_buf.data(), event_buf.size()); |
| if (rv < 0) |
| throw ::std::system_error(errno, ::std::system_category(), |
| "error reading multiple line events"); |
| |
| events.reserve(rv); |
| for (int i = 0; i < rv; i++) |
| events.push_back(this->make_line_event(event_buf[i])); |
| |
| return events; |
| } |
| |
| int line::event_get_fd(void) const |
| { |
| this->throw_if_null(); |
| |
| int ret = ::gpiod_line_event_get_fd(this->_m_line); |
| |
| if (ret < 0) |
| throw ::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::update(void) const |
| { |
| this->throw_if_null(); |
| |
| int ret = ::gpiod_line_update(this->_m_line); |
| |
| if (ret < 0) |
| throw ::std::system_error(errno, ::std::system_category(), |
| "unable to update the line info"); |
| } |
| |
| 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_iter()) { |
| ret = it.find_line(name); |
| if (ret) |
| break; |
| } |
| |
| return ret; |
| } |
| |
| } /* namespace gpiod */ |