#![forbid(future_incompatible)]
#![deny(bad_style, missing_docs)]
#![doc = include_str!("../README.md")]
use std::{
fmt, fs,
fs::{File, OpenOptions},
io::Read,
marker::PhantomData,
ops::Deref,
os::unix::{
fs::{FileTypeExt, MetadataExt},
io::{AsRawFd, FromRawFd},
},
path::{Path, PathBuf},
};
use gpiod_core::{invalid_input, major, minor, AsDevicePath, Internal, Result};
pub use gpiod_core::{
Active, AsValues, AsValuesMut, Bias, BitId, ChipInfo, Direction, DirectionType, Drive, Edge,
EdgeDetect, Event, Input, LineId, LineInfo, Masked, Options, Output, Values, ValuesInfo,
MAX_BITS, MAX_VALUES,
};
pub struct Lines<Direction> {
dir: PhantomData<Direction>,
info: Internal<ValuesInfo>,
file: File,
}
impl<Direction> Deref for Lines<Direction> {
type Target = ValuesInfo;
fn deref(&self) -> &Self::Target {
&self.info
}
}
impl<Direction: DirectionType> Lines<Direction> {
pub fn get_values<T: AsValuesMut>(&self, mut values: T) -> Result<T> {
self.info.get_values(self.file.as_raw_fd(), &mut values)?;
Ok(values)
}
}
impl Lines<Input> {
pub fn read_event(&mut self) -> Result<Event> {
#[cfg(not(feature = "v2"))]
{
todo!();
}
#[cfg(feature = "v2")]
{
let mut event = gpiod_core::RawEvent::default();
gpiod_core::check_size(self.file.read(event.as_mut())?, &event)?;
event.as_event(self.info.index())
}
}
}
impl Iterator for Lines<Input> {
type Item = Result<Event>;
fn next(&mut self) -> Option<Self::Item> {
Some(self.read_event())
}
}
impl Lines<Output> {
pub fn set_values<T: AsValues>(&self, values: T) -> Result<()> {
self.info.set_values(self.file.as_raw_fd(), values)
}
}
pub struct Chip {
info: Internal<ChipInfo>,
file: File,
}
impl Deref for Chip {
type Target = ChipInfo;
fn deref(&self) -> &Self::Target {
&self.info
}
}
impl fmt::Display for Chip {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.info.fmt(f)
}
}
impl Chip {
pub fn new(device: impl AsDevicePath) -> Result<Chip> {
let path = device.as_device_path();
Chip::check_device(&path)?;
let file = OpenOptions::new().read(true).write(true).open(path)?;
Ok(Chip {
info: Internal::<ChipInfo>::from_fd(file.as_raw_fd())?,
file,
})
}
pub fn list_devices() -> Result<Vec<PathBuf>> {
Ok(fs::read_dir("/dev")?
.filter_map(Result::ok)
.map(|ent| ent.path())
.filter(|path| Self::check_device(path).is_ok())
.collect())
}
fn check_device(path: &Path) -> Result<()> {
let metadata = fs::symlink_metadata(path)?;
if !metadata.file_type().is_char_device() {
return Err(invalid_input("File is not character device"));
}
let rdev = metadata.rdev();
if fs::canonicalize(format!(
"/sys/dev/char/{}:{}/subsystem",
major(rdev),
minor(rdev)
))? != Path::new("/sys/bus/gpio")
{
return Err(invalid_input("Character device is not a GPIO"));
}
Ok(())
}
pub fn line_info(&self, line: LineId) -> Result<LineInfo> {
self.info.line_info(self.file.as_raw_fd(), line)
}
pub fn request_lines<Direction: DirectionType>(
&self,
options: Options<Direction, impl AsRef<[LineId]>, impl AsRef<str>>,
) -> Result<Lines<Direction>> {
let (info, fd) = self.info.request_lines(self.file.as_raw_fd(), options)?;
let file = unsafe { File::from_raw_fd(fd) };
Ok(Lines {
dir: PhantomData,
info,
file,
})
}
}