blob: 26354e5b4eee57c3fe19e0932421db9cc09d44b6 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
// SPDX-FileCopyrightText: 2022 Linaro Ltd.
// SPDX-FileCopyrightText: 2022 Viresh Kumar <viresh.kumar@linaro.org>
//
// Rust wrappers for GPIOD APIs
//! libgpiod public API
//!
//! This is the complete documentation of the public Rust API made available to
//! users of libgpiod.
//!
//! The API is logically split into several parts such as: GPIO chip & line
//! operators, GPIO events handling etc.
use std::ffi::CStr;
use std::fs;
use std::os::raw::c_char;
use std::path::Path;
use std::time::Duration;
use std::{fmt, str};
use intmap::IntMap;
use thiserror::Error as ThisError;
use libgpiod_sys as gpiod;
use gpiod::{
gpiod_edge_event_type_GPIOD_EDGE_EVENT_FALLING_EDGE as GPIOD_EDGE_EVENT_FALLING_EDGE,
gpiod_edge_event_type_GPIOD_EDGE_EVENT_RISING_EDGE as GPIOD_EDGE_EVENT_RISING_EDGE,
gpiod_info_event_type_GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED as GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED,
gpiod_info_event_type_GPIOD_INFO_EVENT_LINE_RELEASED as GPIOD_INFO_EVENT_LINE_RELEASED,
gpiod_info_event_type_GPIOD_INFO_EVENT_LINE_REQUESTED as GPIOD_INFO_EVENT_LINE_REQUESTED,
gpiod_line_bias_GPIOD_LINE_BIAS_AS_IS as GPIOD_LINE_BIAS_AS_IS,
gpiod_line_bias_GPIOD_LINE_BIAS_DISABLED as GPIOD_LINE_BIAS_DISABLED,
gpiod_line_bias_GPIOD_LINE_BIAS_PULL_DOWN as GPIOD_LINE_BIAS_PULL_DOWN,
gpiod_line_bias_GPIOD_LINE_BIAS_PULL_UP as GPIOD_LINE_BIAS_PULL_UP,
gpiod_line_bias_GPIOD_LINE_BIAS_UNKNOWN as GPIOD_LINE_BIAS_UNKNOWN,
gpiod_line_clock_GPIOD_LINE_CLOCK_HTE as GPIOD_LINE_CLOCK_HTE,
gpiod_line_clock_GPIOD_LINE_CLOCK_MONOTONIC as GPIOD_LINE_CLOCK_MONOTONIC,
gpiod_line_clock_GPIOD_LINE_CLOCK_REALTIME as GPIOD_LINE_CLOCK_REALTIME,
gpiod_line_direction_GPIOD_LINE_DIRECTION_AS_IS as GPIOD_LINE_DIRECTION_AS_IS,
gpiod_line_direction_GPIOD_LINE_DIRECTION_INPUT as GPIOD_LINE_DIRECTION_INPUT,
gpiod_line_direction_GPIOD_LINE_DIRECTION_OUTPUT as GPIOD_LINE_DIRECTION_OUTPUT,
gpiod_line_drive_GPIOD_LINE_DRIVE_OPEN_DRAIN as GPIOD_LINE_DRIVE_OPEN_DRAIN,
gpiod_line_drive_GPIOD_LINE_DRIVE_OPEN_SOURCE as GPIOD_LINE_DRIVE_OPEN_SOURCE,
gpiod_line_drive_GPIOD_LINE_DRIVE_PUSH_PULL as GPIOD_LINE_DRIVE_PUSH_PULL,
gpiod_line_edge_GPIOD_LINE_EDGE_BOTH as GPIOD_LINE_EDGE_BOTH,
gpiod_line_edge_GPIOD_LINE_EDGE_FALLING as GPIOD_LINE_EDGE_FALLING,
gpiod_line_edge_GPIOD_LINE_EDGE_NONE as GPIOD_LINE_EDGE_NONE,
gpiod_line_edge_GPIOD_LINE_EDGE_RISING as GPIOD_LINE_EDGE_RISING,
gpiod_line_value_GPIOD_LINE_VALUE_ACTIVE as GPIOD_LINE_VALUE_ACTIVE,
gpiod_line_value_GPIOD_LINE_VALUE_ERROR as GPIOD_LINE_VALUE_ERROR,
gpiod_line_value_GPIOD_LINE_VALUE_INACTIVE as GPIOD_LINE_VALUE_INACTIVE,
};
/// Operation types, used with OperationFailed() Error.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum OperationType {
ChipOpen,
ChipWaitInfoEvent,
ChipGetLine,
ChipGetLineInfo,
ChipGetLineOffsetFromName,
ChipGetInfo,
ChipReadInfoEvent,
ChipRequestLines,
ChipWatchLineInfo,
EdgeEventBufferGetEvent,
EdgeEventCopy,
EdgeEventBufferNew,
InfoEventGetLineInfo,
LineConfigNew,
LineConfigAddSettings,
LineConfigSetOutputValues,
LineConfigGetOffsets,
LineConfigGetSettings,
LineRequestReconfigLines,
LineRequestGetVal,
LineRequestGetValSubset,
LineRequestSetVal,
LineRequestSetValSubset,
LineRequestReadEdgeEvent,
LineRequestWaitEdgeEvent,
LineSettingsNew,
LineSettingsCopy,
LineSettingsGetOutVal,
LineSettingsSetDirection,
LineSettingsSetEdgeDetection,
LineSettingsSetBias,
LineSettingsSetDrive,
LineSettingsSetActiveLow,
LineSettingsSetDebouncePeriod,
LineSettingsSetEventClock,
LineSettingsSetOutputValue,
RequestConfigNew,
RequestConfigGetConsumer,
SimBankGetVal,
SimBankNew,
SimBankSetLabel,
SimBankSetNumLines,
SimBankSetLineName,
SimBankSetPull,
SimBankHogLine,
SimCtxNew,
SimDevNew,
SimDevEnable,
SimDevDisable,
}
impl fmt::Display for OperationType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
/// Result of libgpiod operations.
pub type Result<T> = std::result::Result<T, Error>;
/// Error codes for libgpiod operations.
#[derive(Copy, Clone, Debug, Eq, PartialEq, ThisError)]
pub enum Error {
#[error("Failed to get {0}")]
NullString(&'static str),
#[error("String not utf8: {0:?}")]
StringNotUtf8(str::Utf8Error),
#[error("Invalid String")]
InvalidString,
#[error("Invalid enum {0} value: {1}")]
InvalidEnumValue(&'static str, i32),
#[error("Operation {0} Failed: {1}")]
OperationFailed(OperationType, errno::Errno),
#[error("Invalid Arguments")]
InvalidArguments,
#[error("Event count more than buffer capacity: {0} > {1}")]
TooManyEvents(usize, usize),
#[error("Std Io Error")]
IoError,
}
mod info_event;
/// GPIO chip related definitions.
pub mod chip;
mod edge_event;
mod event_buffer;
mod line_request;
mod request_config;
/// GPIO chip request related definitions.
pub mod request {
pub use crate::edge_event::*;
pub use crate::event_buffer::*;
pub use crate::line_request::*;
pub use crate::request_config::*;
}
mod line_config;
mod line_info;
mod line_settings;
/// GPIO chip line related definitions.
pub mod line {
pub use crate::line_config::*;
pub use crate::line_info::*;
pub use crate::line_settings::*;
use super::*;
/// Value settings.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Value {
/// Active
Active,
/// Inactive
InActive,
}
/// Maps offset to Value.
pub type ValueMap = IntMap<Value>;
/// Maps offsets to Settings
pub type SettingsMap = IntMap<Settings>;
impl Value {
pub fn new(val: gpiod::gpiod_line_value) -> Result<Self> {
Ok(match val {
GPIOD_LINE_VALUE_INACTIVE => Value::InActive,
GPIOD_LINE_VALUE_ACTIVE => Value::Active,
GPIOD_LINE_VALUE_ERROR => {
return Err(Error::OperationFailed(
OperationType::LineRequestGetVal,
errno::errno(),
))
}
_ => return Err(Error::InvalidEnumValue("Value", val as i32)),
})
}
pub(crate) fn value(&self) -> gpiod::gpiod_line_value {
match self {
Value::Active => GPIOD_LINE_VALUE_ACTIVE,
Value::InActive => GPIOD_LINE_VALUE_INACTIVE,
}
}
}
/// Offset type.
pub type Offset = u32;
/// Direction settings.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Direction {
/// Request the line(s), but don't change direction.
AsIs,
/// Direction is input - for reading the value of an externally driven GPIO line.
Input,
/// Direction is output - for driving the GPIO line.
Output,
}
impl Direction {
pub(crate) fn new(dir: gpiod::gpiod_line_direction) -> Result<Self> {
Ok(match dir {
GPIOD_LINE_DIRECTION_AS_IS => Direction::AsIs,
GPIOD_LINE_DIRECTION_INPUT => Direction::Input,
GPIOD_LINE_DIRECTION_OUTPUT => Direction::Output,
_ => return Err(Error::InvalidEnumValue("Direction", dir as i32)),
})
}
pub(crate) fn gpiod_direction(&self) -> gpiod::gpiod_line_direction {
match self {
Direction::AsIs => GPIOD_LINE_DIRECTION_AS_IS,
Direction::Input => GPIOD_LINE_DIRECTION_INPUT,
Direction::Output => GPIOD_LINE_DIRECTION_OUTPUT,
}
}
}
/// Internal bias settings.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Bias {
/// The internal bias is disabled.
Disabled,
/// The internal pull-up bias is enabled.
PullUp,
/// The internal pull-down bias is enabled.
PullDown,
}
impl Bias {
pub(crate) fn new(bias: gpiod::gpiod_line_bias) -> Result<Option<Self>> {
Ok(match bias {
GPIOD_LINE_BIAS_UNKNOWN => None,
GPIOD_LINE_BIAS_AS_IS => None,
GPIOD_LINE_BIAS_DISABLED => Some(Bias::Disabled),
GPIOD_LINE_BIAS_PULL_UP => Some(Bias::PullUp),
GPIOD_LINE_BIAS_PULL_DOWN => Some(Bias::PullDown),
_ => return Err(Error::InvalidEnumValue("Bias", bias as i32)),
})
}
pub(crate) fn gpiod_bias(bias: Option<Bias>) -> gpiod::gpiod_line_bias {
match bias {
None => GPIOD_LINE_BIAS_AS_IS,
Some(bias) => match bias {
Bias::Disabled => GPIOD_LINE_BIAS_DISABLED,
Bias::PullUp => GPIOD_LINE_BIAS_PULL_UP,
Bias::PullDown => GPIOD_LINE_BIAS_PULL_DOWN,
},
}
}
}
/// Drive settings.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Drive {
/// Drive setting is push-pull.
PushPull,
/// Line output is open-drain.
OpenDrain,
/// Line output is open-source.
OpenSource,
}
impl Drive {
pub(crate) fn new(drive: gpiod::gpiod_line_drive) -> Result<Self> {
Ok(match drive {
GPIOD_LINE_DRIVE_PUSH_PULL => Drive::PushPull,
GPIOD_LINE_DRIVE_OPEN_DRAIN => Drive::OpenDrain,
GPIOD_LINE_DRIVE_OPEN_SOURCE => Drive::OpenSource,
_ => return Err(Error::InvalidEnumValue("Drive", drive as i32)),
})
}
pub(crate) fn gpiod_drive(&self) -> gpiod::gpiod_line_drive {
match self {
Drive::PushPull => GPIOD_LINE_DRIVE_PUSH_PULL,
Drive::OpenDrain => GPIOD_LINE_DRIVE_OPEN_DRAIN,
Drive::OpenSource => GPIOD_LINE_DRIVE_OPEN_SOURCE,
}
}
}
/// Edge detection settings.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Edge {
/// Line detects rising edge events.
Rising,
/// Line detects falling edge events.
Falling,
/// Line detects both rising and falling edge events.
Both,
}
impl Edge {
pub(crate) fn new(edge: gpiod::gpiod_line_edge) -> Result<Option<Self>> {
Ok(match edge {
GPIOD_LINE_EDGE_NONE => None,
GPIOD_LINE_EDGE_RISING => Some(Edge::Rising),
GPIOD_LINE_EDGE_FALLING => Some(Edge::Falling),
GPIOD_LINE_EDGE_BOTH => Some(Edge::Both),
_ => return Err(Error::InvalidEnumValue("Edge", edge as i32)),
})
}
pub(crate) fn gpiod_edge(edge: Option<Edge>) -> gpiod::gpiod_line_edge {
match edge {
None => GPIOD_LINE_EDGE_NONE,
Some(edge) => match edge {
Edge::Rising => GPIOD_LINE_EDGE_RISING,
Edge::Falling => GPIOD_LINE_EDGE_FALLING,
Edge::Both => GPIOD_LINE_EDGE_BOTH,
},
}
}
}
/// Line setting kind.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SettingKind {
/// Line direction.
Direction,
/// Bias.
Bias,
/// Drive.
Drive,
/// Edge detection.
EdgeDetection,
/// Active-low setting.
ActiveLow,
/// Debounce period.
DebouncePeriod,
/// Event clock type.
EventClock,
/// Output value.
OutputValue,
}
/// Line settings.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SettingVal {
/// Line direction.
Direction(Direction),
/// Bias.
Bias(Option<Bias>),
/// Drive.
Drive(Drive),
/// Edge detection.
EdgeDetection(Option<Edge>),
/// Active-low setting.
ActiveLow(bool),
/// Debounce period.
DebouncePeriod(Duration),
/// Event clock type.
EventClock(EventClock),
/// Output value.
OutputValue(Value),
}
impl fmt::Display for SettingVal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
/// Event clock settings.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum EventClock {
/// Line uses the monotonic clock for edge event timestamps.
Monotonic,
/// Line uses the realtime clock for edge event timestamps.
Realtime,
/// Line uses the hardware timestamp engine clock for edge event timestamps.
HTE,
}
impl EventClock {
pub(crate) fn new(clock: gpiod::gpiod_line_clock) -> Result<Self> {
Ok(match clock {
GPIOD_LINE_CLOCK_MONOTONIC => EventClock::Monotonic,
GPIOD_LINE_CLOCK_REALTIME => EventClock::Realtime,
GPIOD_LINE_CLOCK_HTE => EventClock::HTE,
_ => return Err(Error::InvalidEnumValue("Eventclock", clock as i32)),
})
}
pub(crate) fn gpiod_clock(&self) -> gpiod::gpiod_line_clock {
match self {
EventClock::Monotonic => GPIOD_LINE_CLOCK_MONOTONIC,
EventClock::Realtime => GPIOD_LINE_CLOCK_REALTIME,
EventClock::HTE => GPIOD_LINE_CLOCK_HTE,
}
}
}
/// Line status change event types.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum InfoChangeKind {
/// Line has been requested.
LineRequested,
/// Previously requested line has been released.
LineReleased,
/// Line configuration has changed.
LineConfigChanged,
}
impl InfoChangeKind {
pub(crate) fn new(kind: gpiod::gpiod_info_event_type) -> Result<Self> {
Ok(match kind {
GPIOD_INFO_EVENT_LINE_REQUESTED => InfoChangeKind::LineRequested,
GPIOD_INFO_EVENT_LINE_RELEASED => InfoChangeKind::LineReleased,
GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED => InfoChangeKind::LineConfigChanged,
_ => return Err(Error::InvalidEnumValue("InfoChangeKind", kind as i32)),
})
}
}
/// Edge event types.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum EdgeKind {
/// Rising edge event.
Rising,
/// Falling edge event.
Falling,
}
impl EdgeKind {
pub(crate) fn new(kind: gpiod::gpiod_edge_event_type) -> Result<Self> {
Ok(match kind {
GPIOD_EDGE_EVENT_RISING_EDGE => EdgeKind::Rising,
GPIOD_EDGE_EVENT_FALLING_EDGE => EdgeKind::Falling,
_ => return Err(Error::InvalidEnumValue("EdgeEvent", kind as i32)),
})
}
}
}
/// Various libgpiod-related functions.
/// Check if the file pointed to by path is a GPIO chip character device.
///
/// Returns true if the file exists and is a GPIO chip character device or a
/// symbolic link to it.
pub fn is_gpiochip_device<P: AsRef<Path>>(path: &P) -> bool {
// Null-terminate the string
let path = path.as_ref().to_string_lossy() + "\0";
// SAFETY: libgpiod won't access the path reference once the call returns.
unsafe { gpiod::gpiod_is_gpiochip_device(path.as_ptr() as *const c_char) }
}
/// GPIO devices.
///
/// Returns a vector of unique available GPIO Chips.
///
/// The chips are sorted in ascending order of the chip names.
pub fn gpiochip_devices<P: AsRef<Path>>(path: &P) -> Result<Vec<chip::Chip>> {
let mut devices = Vec::new();
for entry in fs::read_dir(path).map_err(|_| Error::IoError)?.flatten() {
let path = entry.path();
if is_gpiochip_device(&path) {
let chip = chip::Chip::open(&path)?;
let info = chip.info()?;
devices.push((chip, info));
}
}
devices.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
devices.dedup_by(|a, b| a.1.eq(&b.1));
Ok(devices.into_iter().map(|a| a.0).collect())
}
/// Get the API version of the libgpiod library as a human-readable string.
pub fn libgpiod_version() -> Result<&'static str> {
// SAFETY: The string returned by libgpiod is guaranteed to live forever.
let version = unsafe { gpiod::gpiod_api_version() };
if version.is_null() {
return Err(Error::NullString("GPIO library version"));
}
// SAFETY: The string is guaranteed to be valid here by the C API.
unsafe { CStr::from_ptr(version) }
.to_str()
.map_err(Error::StringNotUtf8)
}
/// Get the API version of the libgpiod crate as a human-readable string.
pub fn crate_version() -> &'static str {
env!("CARGO_PKG_VERSION")
}