#[cfg(not(any(feature = "uapi_v1", feature = "uapi_v2")))]
compile_error!("Either feature \"uapi_v1\" or \"uapi_v2\" must be enabled for this crate.");
#[cfg(any(feature = "uapi_v1", feature = "uapi_v2"))]
use gpiocdev_uapi as uapi;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::ops::Range;
use std::path::PathBuf;
pub mod chip;
pub use chip::Chip;
pub mod line;
#[cfg(any(feature = "async_tokio", feature = "async_io"))]
mod r#async;
#[cfg(feature = "async_io")]
pub use r#async::async_io;
#[cfg(feature = "async_tokio")]
pub use r#async::tokio;
pub fn lines() -> Result<LineIterator> {
LineIterator::new()
}
pub fn find_named_line(name: &str) -> Option<FoundLine> {
if let Ok(mut liter) = LineIterator::new() {
return liter.find(|l| l.info.name == name);
}
None
}
pub fn find_named_lines<'a>(
names: &'a [&'a str],
strict: bool,
) -> Result<HashMap<&'a str, FoundLine>> {
let mut found = HashMap::new();
for l in LineIterator::new()? {
for name in names {
if *name != l.info.name.as_str() {
continue;
}
if !found.contains_key(*name) {
found.insert(name.to_owned(), l.clone());
if !strict && found.len() == names.len() {
return Ok(found);
}
} else if strict {
return Err(Error::NonuniqueLineName(name.to_string()));
}
}
}
Ok(found)
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct FoundLine {
pub chip: PathBuf,
pub info: line::Info,
}
#[cfg(test)]
impl From<line::Offset> for FoundLine {
fn from(offset: line::Offset) -> Self {
let mut f = FoundLine::default();
f.info.offset = offset;
f
}
}
pub struct LineIterator {
chips: Vec<PathBuf>,
citer: Range<usize>,
chip: chip::Chip,
liter: Range<u32>,
}
fn next_chip(chips: &[PathBuf], citer: &mut Range<usize>) -> Option<(chip::Chip, Range<u32>)> {
for cidx in citer {
if let Ok(chip) = chip::Chip::from_path(&chips[cidx]) {
if let Ok(info) = chip.info() {
return Some((
chip,
Range {
start: 0,
end: info.num_lines,
},
));
}
}
}
None
}
impl LineIterator {
pub fn new() -> Result<Self> {
let chips = chip::chips()?;
let mut citer = Range {
start: 0,
end: chips.len(),
};
if let Some((chip, liter)) = next_chip(&chips, &mut citer) {
Ok(LineIterator {
chips,
citer,
chip,
liter,
})
} else {
Err(Error::NoGpioChips())
}
}
fn next_line_info(&mut self) -> Option<line::Info> {
for offset in &mut self.liter {
if let Ok(linfo) = self.chip.line_info(offset) {
return Some(linfo);
}
}
None
}
}
impl Iterator for LineIterator {
type Item = FoundLine;
fn next(&mut self) -> Option<FoundLine> {
if let Some(linfo) = self.next_line_info() {
return Some(FoundLine {
chip: self.chip.path().to_path_buf(),
info: linfo,
});
}
if let Some((chip, liter)) = next_chip(&self.chips, &mut self.citer) {
self.chip = chip;
self.liter = liter;
if let Some(linfo) = self.next_line_info() {
return Some(FoundLine {
chip: self.chip.path().to_path_buf(),
info: linfo,
});
}
}
None
}
}
pub mod request;
pub use request::Request;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub enum AbiVersion {
V1,
#[default]
V2,
}
impl fmt::Display for AbiVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AbiVersion::V1 => write!(f, "uAPI ABI v1"),
AbiVersion::V2 => write!(f, "uAPI ABI v2"),
}
}
}
#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
pub enum Error {
#[error("{0} {1}.")]
AbiLimitation(AbiVersion, String),
#[error("\"{0}\" {1}.")]
GpioChip(PathBuf, chip::ErrorKind),
#[error("{0}")]
InvalidArgument(String),
#[error("No GPIO chips are available")]
NoGpioChips(),
#[error("Line name '{0}' is not unique")]
NonuniqueLineName(String),
#[error(transparent)]
Os(uapi::Errno),
#[error("uAPI {0} returned: {1}")]
Uapi(UapiCall, #[source] uapi::Error),
#[error("field '{0}' contains unexpected value '{1}'")]
UnexpectedResponse(UapiField, String),
#[error("{0} is not supported by the {1}.")]
UnsupportedAbi(AbiVersion, AbiSupportKind),
#[error("uAPI ABI is not supported by the kernel.")]
NoAbiSupport(),
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Os(uapi::Errno::from(&e))
}
}
#[doc(hidden)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UapiCall {
GetChipInfo,
GetLine,
GetLineEvent,
GetLineHandle,
GetLineInfo,
GetLineValues,
HasEvent,
LEEFromBuf,
LICEFromBuf,
ReadEvent,
SetLineConfig,
SetLineValues,
UnwatchLineInfo,
WaitEvent,
WatchLineInfo,
}
impl fmt::Display for UapiCall {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
UapiCall::GetChipInfo => "get_chip_info",
UapiCall::GetLine => "get_line",
UapiCall::GetLineEvent => "get_line_event",
UapiCall::GetLineHandle => "get_line_handle",
UapiCall::GetLineInfo => "get_line_info",
UapiCall::GetLineValues => "get_line_values",
UapiCall::HasEvent => "has_event",
UapiCall::LEEFromBuf => "LineEdgeEvent::from_buf",
UapiCall::LICEFromBuf => "LineInfoChangeEvent::from_buf",
UapiCall::ReadEvent => "read_event",
UapiCall::SetLineConfig => "set_line_config",
UapiCall::SetLineValues => "set_line_values",
UapiCall::UnwatchLineInfo => "unwatch_line_info",
UapiCall::WaitEvent => "wait_event",
UapiCall::WatchLineInfo => "watch_line_info",
};
write!(f, "{name}")
}
}
#[doc(hidden)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UapiField {
Kind,
NumAttrs,
}
impl fmt::Display for UapiField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
UapiField::Kind => "kind",
UapiField::NumAttrs => "num_attrs",
};
write!(f, "{name}")
}
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum AbiSupportKind {
Build,
#[default]
Kernel,
}
impl fmt::Display for AbiSupportKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AbiSupportKind::Build => write!(f, "build"),
AbiSupportKind::Kernel => write!(f, "kernel"),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub fn detect_abi_version() -> Result<AbiVersion> {
for p in chip::chips()? {
if let Ok(c) = chip::Chip::from_path(p) {
return c.detect_abi_version();
}
}
Err(Error::NoGpioChips())
}
pub fn supports_abi_version(abiv: AbiVersion) -> Result<()> {
for p in chip::chips()? {
if let Ok(c) = chip::Chip::from_path(p) {
return c.supports_abi_version(abiv);
}
}
Err(Error::NoGpioChips())
}
#[cfg(test)]
mod tests {
use super::*;
mod uapi_call {
#[test]
fn display() {
use super::UapiCall;
let uc = UapiCall::GetChipInfo;
assert_eq!(format!("{uc}"), "get_chip_info");
let uc = UapiCall::GetLine;
assert_eq!(format!("{uc}"), "get_line");
let uc = UapiCall::GetLineEvent;
assert_eq!(format!("{uc}"), "get_line_event");
let uc = UapiCall::GetLineHandle;
assert_eq!(format!("{uc}"), "get_line_handle");
let uc = UapiCall::GetLineInfo;
assert_eq!(format!("{uc}"), "get_line_info");
let uc = UapiCall::GetLineValues;
assert_eq!(format!("{uc}"), "get_line_values");
let uc = UapiCall::HasEvent;
assert_eq!(format!("{uc}"), "has_event");
let uc = UapiCall::LEEFromBuf;
assert_eq!(format!("{uc}"), "LineEdgeEvent::from_buf");
let uc = UapiCall::LICEFromBuf;
assert_eq!(format!("{uc}"), "LineInfoChangeEvent::from_buf");
let uc = UapiCall::ReadEvent;
assert_eq!(format!("{uc}"), "read_event");
let uc = UapiCall::SetLineConfig;
assert_eq!(format!("{uc}"), "set_line_config");
let uc = UapiCall::SetLineValues;
assert_eq!(format!("{uc}"), "set_line_values");
let uc = UapiCall::WaitEvent;
assert_eq!(format!("{uc}"), "wait_event");
let uc = UapiCall::WatchLineInfo;
assert_eq!(format!("{uc}"), "watch_line_info");
let uc = UapiCall::UnwatchLineInfo;
assert_eq!(format!("{uc}"), "unwatch_line_info");
}
}
mod abi_support_kind {
#[test]
fn display() {
use crate::AbiSupportKind;
let ask = AbiSupportKind::Build;
assert_eq!(format!("{ask}"), "build");
let ask = AbiSupportKind::Kernel;
assert_eq!(format!("{ask}"), "kernel");
}
}
}