mod builder;
pub use self::builder::Builder;
mod config;
pub use self::config::Config;
mod edge_event_buffer;
pub use self::edge_event_buffer::EdgeEventBuffer;
use crate::line::{self, EdgeEvent, Offset, Value, Values};
#[cfg(feature = "uapi_v1")]
use crate::AbiVersion;
use crate::{Error, Result, UapiCall};
#[cfg(not(feature = "uapi_v2"))]
use gpiocdev_uapi::v1 as uapi;
#[cfg(feature = "uapi_v1")]
use gpiocdev_uapi::v1;
#[cfg(feature = "uapi_v2")]
use gpiocdev_uapi::{v2, v2 as uapi};
use std::fs::File;
use std::mem;
use std::os::unix::prelude::{AsFd, AsRawFd, BorrowedFd};
use std::sync::{Arc, RwLock};
use std::time::Duration;
#[derive(Debug)]
pub struct Request {
f: File,
offsets: Vec<Offset>,
cfg: Arc<RwLock<Config>>,
user_event_buffer_size: usize,
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
abiv: AbiVersion,
}
impl Request {
pub fn builder() -> Builder {
Builder::default()
}
pub fn from_config(config: Config) -> Builder {
Builder::from_config(config)
}
pub fn values(&self, values: &mut Values) -> Result<()> {
self.do_values(values)
}
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
fn do_values(&self, values: &mut Values) -> Result<()> {
match self.abiv {
AbiVersion::V1 => self.do_values_v1(values),
AbiVersion::V2 => self.do_values_v2(values),
}
}
#[cfg(not(feature = "uapi_v2"))]
fn do_values(&self, values: &mut Values) -> Result<()> {
self.do_values_v1(values)
}
#[cfg(not(feature = "uapi_v1"))]
fn do_values(&self, values: &mut Values) -> Result<()> {
self.do_values_v2(values)
}
#[cfg(feature = "uapi_v1")]
fn do_values_v1(&self, values: &mut Values) -> Result<()> {
let mut vals = v1::LineValues::default();
v1::get_line_values(&self.f, &mut vals)
.map(|_| values.update_from_v1(&self.offsets, &vals))
.map_err(|e| Error::Uapi(UapiCall::GetLineValues, e))
}
#[cfg(feature = "uapi_v2")]
fn do_values_v2(&self, values: &mut Values) -> Result<()> {
let mut vals = values.to_v2(&self.offsets);
v2::get_line_values(&self.f, &mut vals)
.map(|_| values.update_from_v2(&self.offsets, &vals))
.map_err(|e| Error::Uapi(UapiCall::GetLineValues, e))
}
pub fn value(&self, offset: Offset) -> Result<Value> {
let idx = self
.offsets
.iter()
.position(|v| v == &offset)
.ok_or_else(|| Error::InvalidArgument("offset is not a requested line.".into()))?;
self.do_value(idx)
}
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
fn do_value(&self, idx: usize) -> Result<Value> {
match self.abiv {
AbiVersion::V1 => self.do_value_v1(idx),
AbiVersion::V2 => self.do_value_v2(idx),
}
}
#[cfg(not(feature = "uapi_v2"))]
fn do_value(&self, idx: usize) -> Result<Value> {
self.do_value_v1(idx)
}
#[cfg(not(feature = "uapi_v1"))]
fn do_value(&self, idx: usize) -> Result<Value> {
self.do_value_v2(idx)
}
#[cfg(feature = "uapi_v1")]
fn do_value_v1(&self, idx: usize) -> Result<Value> {
let mut vals = v1::LineValues::default();
v1::get_line_values(&self.f, &mut vals)
.map_err(|e| Error::Uapi(UapiCall::GetLineValues, e))?;
Ok(vals.get(idx).into())
}
#[cfg(feature = "uapi_v2")]
fn do_value_v2(&self, idx: usize) -> Result<Value> {
let mut vals = v2::LineValues {
mask: 0x01 << idx,
..Default::default()
};
v2::get_line_values(&self.f, &mut vals)
.map_err(|e| Error::Uapi(UapiCall::GetLineValues, e))?;
Ok(vals.get(idx).unwrap().into())
}
pub fn lone_value(&self) -> Result<Value> {
if self.offsets.len() != 1 {
return Err(Error::InvalidArgument(
"request contains multiple lines.".into(),
))?;
}
self.do_value(0)
}
pub fn set_values(&self, values: &Values) -> Result<()> {
self.do_set_values(values)
}
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
fn do_set_values(&self, values: &Values) -> Result<()> {
match self.abiv {
AbiVersion::V1 => self.do_set_values_v1(values),
AbiVersion::V2 => self.do_set_values_v2(values),
}
}
#[cfg(not(feature = "uapi_v2"))]
fn do_set_values(&self, values: &Values) -> Result<()> {
self.do_set_values_v1(values)
}
#[cfg(not(feature = "uapi_v1"))]
fn do_set_values(&self, values: &Values) -> Result<()> {
self.do_set_values_v2(values)
}
#[cfg(feature = "uapi_v1")]
fn do_set_values_v1(&self, values: &Values) -> Result<()> {
if !values.contains_keys(&self.offsets) {
return Err(Error::AbiLimitation(
AbiVersion::V1,
"requires all requested lines".into(),
));
}
v1::set_line_values(&self.f, &values.to_v1(&self.offsets))
.map_err(|e| Error::Uapi(UapiCall::SetLineValues, e))
}
#[cfg(feature = "uapi_v2")]
fn do_set_values_v2(&self, values: &Values) -> Result<()> {
let lv = &values.to_v2(&self.offsets);
if lv.mask == 0 {
return Err(Error::InvalidArgument(
"no requested lines in set values.".into(),
));
}
v2::set_line_values(&self.f, lv).map_err(|e| Error::Uapi(UapiCall::SetLineValues, e))
}
pub fn set_value(&self, offset: Offset, value: Value) -> Result<()> {
let idx = self
.offsets
.iter()
.position(|v| v == &offset)
.ok_or_else(|| Error::InvalidArgument("offset is not a requested line.".into()))?;
self.do_set_value(idx, value)
}
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
fn do_set_value(&self, idx: usize, value: Value) -> Result<()> {
match self.abiv {
AbiVersion::V1 => self.do_set_value_v1(idx, value),
AbiVersion::V2 => self.do_set_value_v2(idx, value),
}
}
#[cfg(not(feature = "uapi_v2"))]
fn do_set_value(&self, idx: usize, value: Value) -> Result<()> {
self.do_set_value_v1(idx, value)
}
#[cfg(not(feature = "uapi_v1"))]
fn do_set_value(&self, idx: usize, value: Value) -> Result<()> {
self.do_set_value_v2(idx, value)
}
#[cfg(feature = "uapi_v1")]
fn do_set_value_v1(&self, idx: usize, value: Value) -> Result<()> {
if self.offsets.len() > 1 {
return Err(Error::AbiLimitation(
AbiVersion::V1,
"requires all requested lines".into(),
));
}
let mut vals = v1::LineValues::default();
vals.set(idx, value.into());
v1::set_line_values(&self.f, &vals).map_err(|e| Error::Uapi(UapiCall::SetLineValues, e))
}
#[cfg(feature = "uapi_v2")]
fn do_set_value_v2(&self, idx: usize, value: Value) -> Result<()> {
let mut vals = v2::LineValues::default();
let mask = 0x01 << idx;
if value == Value::Active {
vals.bits |= mask;
}
vals.mask |= mask;
v2::set_line_values(&self.f, &vals).map_err(|e| Error::Uapi(UapiCall::SetLineValues, e))
}
pub fn set_lone_value(&self, value: Value) -> Result<()> {
if self.offsets.len() != 1 {
return Err(Error::InvalidArgument(
"request contains multiple lines.".into(),
))?;
}
self.do_set_value(0, value)
}
pub fn chip_path(&self) -> std::path::PathBuf {
self.cfg
.read()
.expect("failed to acquire read lock on config")
.chip
.clone()
}
pub fn config(&self) -> Config {
self.cfg
.read()
.expect("failed to acquire read lock on config")
.clone()
}
pub fn line_config(&self, offset: Offset) -> Option<line::Config> {
self.cfg
.read()
.expect("failed to acquire read lock on config")
.line_config(offset)
.cloned()
}
pub fn reconfigure(&self, new_cfg: &Config) -> Result<()> {
let cfg = self
.cfg
.read()
.expect("failed to acquire read lock on config")
.overlay(new_cfg);
self.do_reconfigure(&cfg)?;
self.cfg
.write()
.expect("failed to acquire write lock on config")
.update(cfg);
Ok(())
}
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
fn do_reconfigure(&self, cfg: &Config) -> Result<()> {
match self.abiv {
AbiVersion::V1 => {
if self
.cfg
.read()
.expect("failed to acquire read lock on config")
.unique()?
.edge_detection
.is_some()
{
return Err(Error::AbiLimitation(
AbiVersion::V1,
"cannot reconfigure lines with edge detection".into(),
));
}
if cfg.unique()?.edge_detection.is_some() {
return Err(Error::AbiLimitation(
AbiVersion::V1,
"cannot reconfigure edge detection".into(),
));
}
v1::set_line_config(&self.f, cfg.to_v1()?)
.map_err(|e| Error::Uapi(UapiCall::SetLineConfig, e))
}
AbiVersion::V2 => v2::set_line_config(&self.f, cfg.to_v2()?)
.map_err(|e| Error::Uapi(UapiCall::SetLineConfig, e)),
}
}
#[cfg(not(feature = "uapi_v2"))]
fn do_reconfigure(&self, cfg: &Config) -> Result<()> {
if self
.cfg
.read()
.expect("failed to acquire read lock on config")
.unique()?
.edge_detection
.is_some()
{
return Err(Error::AbiLimitation(
AbiVersion::V1,
"cannot reconfigure lines with edge detection".into(),
));
}
if cfg.unique()?.edge_detection.is_some() {
return Err(Error::AbiLimitation(
AbiVersion::V1,
"cannot reconfigure edge detection".into(),
));
}
v1::set_line_config(&self.f, cfg.to_v1()?)
.map_err(|e| Error::Uapi(UapiCall::SetLineConfig, e))
}
#[cfg(not(feature = "uapi_v1"))]
fn do_reconfigure(&self, cfg: &Config) -> Result<()> {
v2::set_line_config(&self.f, cfg.to_v2()?)
.map_err(|e| Error::Uapi(UapiCall::SetLineConfig, e))
}
pub fn edge_events(&self) -> EdgeEventBuffer<'_> {
self.new_edge_event_buffer(self.user_event_buffer_size)
}
pub fn has_edge_event(&self) -> Result<bool> {
gpiocdev_uapi::has_event(&self.f).map_err(|e| Error::Uapi(UapiCall::HasEvent, e))
}
pub fn wait_edge_event(&self, timeout: Duration) -> Result<bool> {
gpiocdev_uapi::wait_event(&self.f, timeout).map_err(|e| Error::Uapi(UapiCall::WaitEvent, e))
}
pub fn read_edge_event(&self) -> Result<EdgeEvent> {
self.do_read_edge_event()
}
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
fn do_read_edge_event(&self) -> Result<EdgeEvent> {
let mut bbuf = [0; mem::size_of::<v2::LineEdgeEvent>() / 8];
let buf = &mut bbuf[0..self.edge_event_u64_size()];
let n = self.read_edge_events_into_slice(buf)?;
self.do_edge_event_from_slice(&buf[0..n])
}
#[cfg(not(all(feature = "uapi_v1", feature = "uapi_v2")))]
fn do_read_edge_event(&self) -> Result<EdgeEvent> {
let mut buf = [0; mem::size_of::<uapi::LineEdgeEvent>()];
let n = self.read_edge_events_into_slice(&mut buf)?;
self.do_edge_event_from_slice(&buf[0..n])
}
pub fn new_edge_event_buffer(&self, capacity: usize) -> EdgeEventBuffer<'_> {
EdgeEventBuffer::new(self, self.edge_event_size(), capacity)
}
pub fn read_edge_events_into_slice(&self, buf: &mut [u64]) -> Result<usize> {
gpiocdev_uapi::read_event(&self.f, buf).map_err(|e| Error::Uapi(UapiCall::ReadEvent, e))
}
pub fn edge_event_from_slice(&self, buf: &[u64]) -> Result<EdgeEvent> {
self.do_edge_event_from_slice(buf)
}
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
fn do_edge_event_from_slice(&self, buf: &[u64]) -> Result<EdgeEvent> {
match self.abiv {
AbiVersion::V1 => {
let mut ee = EdgeEvent::try_from(
v1::LineEdgeEvent::from_slice(buf)
.map_err(|e| Error::Uapi(UapiCall::LEEFromBuf, e))?,
)?;
ee.offset = self.offsets[0];
Ok(ee)
}
AbiVersion::V2 => EdgeEvent::try_from(
uapi::LineEdgeEvent::from_slice(buf)
.map_err(|e| Error::Uapi(UapiCall::LEEFromBuf, e))?,
),
}
}
#[cfg(not(feature = "uapi_v2"))]
fn do_edge_event_from_slice(&self, buf: &[u64]) -> Result<EdgeEvent> {
let mut ee = EdgeEvent::try_from(
v1::LineEdgeEvent::from_slice(buf).map_err(|e| Error::Uapi(UapiCall::LEEFromBuf, e))?,
)?;
ee.offset = self.offsets[0]; Ok(ee)
}
#[cfg(not(feature = "uapi_v1"))]
fn do_edge_event_from_slice(&self, buf: &[u64]) -> Result<EdgeEvent> {
EdgeEvent::try_from(
v2::LineEdgeEvent::from_slice(buf).map_err(|e| Error::Uapi(UapiCall::LEEFromBuf, e))?,
)
}
pub fn edge_event_u64_size(&self) -> usize {
self.do_edge_event_size() / 8
}
pub fn edge_event_size(&self) -> usize {
self.do_edge_event_size()
}
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
fn do_edge_event_size(&self) -> usize {
match self.abiv {
AbiVersion::V1 => mem::size_of::<v1::LineEdgeEvent>(),
AbiVersion::V2 => mem::size_of::<v2::LineEdgeEvent>(),
}
}
#[cfg(not(all(feature = "uapi_v1", feature = "uapi_v2")))]
fn do_edge_event_size(&self) -> usize {
mem::size_of::<uapi::LineEdgeEvent>()
}
}
impl AsFd for Request {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.f.as_fd()
}
}
impl AsRawFd for Request {
#[inline]
fn as_raw_fd(&self) -> i32 {
self.f.as_raw_fd()
}
}
impl AsRef<Request> for Request {
#[inline]
fn as_ref(&self) -> &Request {
self
}
}
#[cfg(test)]
mod tests {
use super::Request;
#[test]
fn builder() {
let b = Request::builder();
assert_eq!(b.cfg.chip.as_os_str(), "");
assert_eq!(b.cfg.num_lines(), 0);
assert_eq!(b.consumer.as_str(), "");
assert_eq!(b.kernel_event_buffer_size, 0);
assert_eq!(b.user_event_buffer_size, 0);
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
assert_eq!(b.abiv, None);
}
#[test]
fn from_config() {
use super::line::Value;
use super::Config;
let mut cfg = Config::default();
cfg.with_line(3)
.as_input()
.with_line(5)
.as_output(Value::Active);
let b = Request::from_config(cfg);
assert_eq!(b.cfg.chip.as_os_str(), "");
assert_eq!(b.cfg.num_lines(), 2);
assert_eq!(b.consumer.as_str(), "");
assert_eq!(b.kernel_event_buffer_size, 0);
assert_eq!(b.user_event_buffer_size, 0);
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
assert_eq!(b.abiv, None);
}
}