mod config;
pub use self::config::Config;
mod event;
pub use self::event::{EdgeEvent, EdgeKind, InfoChangeEvent, InfoChangeKind};
mod info;
pub use self::info::Info;
mod value;
pub use self::value::{Value, Values};
#[cfg(feature = "uapi_v1")]
use gpiocdev_uapi::v1;
#[cfg(feature = "uapi_v2")]
use gpiocdev_uapi::v2;
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
use std::hash::{BuildHasherDefault, Hasher};
pub type Offset = u32;
pub type OffsetMap<T> = HashMap<Offset, T, BuildHasherDefault<OffsetHasher>>;
#[derive(Default)]
pub struct OffsetHasher(u64);
impl Hasher for OffsetHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, _: &[u8]) {
panic!("OffsetHasher key must be u32")
}
fn write_u32(&mut self, n: u32) {
self.0 = n.into()
}
}
pub type Offsets = Vec<Offset>;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Direction {
#[default]
Input,
Output,
}
#[cfg(feature = "uapi_v1")]
impl From<v1::LineInfoFlags> for Direction {
fn from(flags: v1::LineInfoFlags) -> Self {
if flags.contains(v1::LineInfoFlags::OUTPUT) {
return Direction::Output;
}
Direction::Input
}
}
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
impl From<v2::LineFlags> for Direction {
fn from(flags: v2::LineFlags) -> Self {
if flags.contains(v2::LineFlags::OUTPUT) {
return Direction::Output;
}
Direction::Input
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Bias {
PullUp,
PullDown,
Disabled,
}
#[cfg(feature = "uapi_v1")]
impl TryFrom<v1::LineInfoFlags> for Bias {
type Error = ();
fn try_from(flags: v1::LineInfoFlags) -> Result<Self, Self::Error> {
if flags.contains(v1::LineInfoFlags::BIAS_PULL_UP) {
return Ok(Bias::PullUp);
}
if flags.contains(v1::LineInfoFlags::BIAS_PULL_DOWN) {
return Ok(Bias::PullDown);
}
if flags.contains(v1::LineInfoFlags::BIAS_DISABLED) {
return Ok(Bias::Disabled);
}
Err(())
}
}
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
impl TryFrom<v2::LineFlags> for Bias {
type Error = ();
fn try_from(flags: v2::LineFlags) -> Result<Self, Self::Error> {
if flags.contains(v2::LineFlags::BIAS_PULL_UP) {
return Ok(Bias::PullUp);
}
if flags.contains(v2::LineFlags::BIAS_PULL_DOWN) {
return Ok(Bias::PullDown);
}
if flags.contains(v2::LineFlags::BIAS_DISABLED) {
return Ok(Bias::Disabled);
}
Err(())
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Drive {
#[default]
PushPull,
OpenDrain,
OpenSource,
}
#[cfg(feature = "uapi_v1")]
impl TryFrom<v1::LineInfoFlags> for Drive {
type Error = ();
fn try_from(flags: v1::LineInfoFlags) -> Result<Self, Self::Error> {
if flags.contains(v1::LineInfoFlags::OPEN_DRAIN) {
return Ok(Drive::OpenDrain);
}
if flags.contains(v1::LineInfoFlags::OPEN_SOURCE) {
return Ok(Drive::OpenSource);
}
if flags.contains(v1::LineInfoFlags::OUTPUT) {
return Ok(Drive::PushPull);
}
Err(())
}
}
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
impl TryFrom<v2::LineFlags> for Drive {
type Error = ();
fn try_from(flags: v2::LineFlags) -> Result<Self, Self::Error> {
if flags.contains(v2::LineFlags::OPEN_DRAIN) {
return Ok(Drive::OpenDrain);
}
if flags.contains(v2::LineFlags::OPEN_SOURCE) {
return Ok(Drive::OpenSource);
}
if flags.contains(v2::LineFlags::OUTPUT) {
return Ok(Drive::PushPull);
}
Err(())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum EdgeDetection {
RisingEdge,
FallingEdge,
BothEdges,
}
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
impl TryFrom<v2::LineFlags> for EdgeDetection {
type Error = ();
fn try_from(flags: v2::LineFlags) -> Result<Self, Self::Error> {
if flags.contains(v2::LineFlags::EDGE_RISING | v2::LineFlags::EDGE_FALLING) {
return Ok(EdgeDetection::BothEdges);
}
if flags.contains(v2::LineFlags::EDGE_RISING) {
return Ok(EdgeDetection::RisingEdge);
}
if flags.contains(v2::LineFlags::EDGE_FALLING) {
return Ok(EdgeDetection::FallingEdge);
}
Err(())
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum EventClock {
#[default]
Monotonic,
Realtime,
Hte,
}
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
impl From<v2::LineFlags> for EventClock {
fn from(flags: v2::LineFlags) -> Self {
if flags.contains(v2::LineFlags::EVENT_CLOCK_REALTIME) {
return EventClock::Realtime;
}
if flags.contains(v2::LineFlags::EVENT_CLOCK_HTE) {
return EventClock::Hte;
}
EventClock::Monotonic
}
}
#[cfg(test)]
mod tests {
use super::*;
mod direction {
use super::*;
#[test]
fn default() {
assert_eq!(Direction::default(), Direction::Input);
}
#[test]
#[cfg(feature = "uapi_v1")]
fn from_v1_line_info_flags() {
assert_eq!(
Direction::from(v1::LineInfoFlags::OUTPUT),
Direction::Output
);
assert_eq!(
Direction::from(v1::LineInfoFlags::ACTIVE_LOW),
Direction::Input
);
}
#[test]
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
fn from_v2_line_flags() {
assert_eq!(Direction::from(v2::LineFlags::OUTPUT), Direction::Output);
assert_eq!(Direction::from(v2::LineFlags::INPUT), Direction::Input);
}
}
mod bias {
use super::*;
#[test]
#[cfg(feature = "uapi_v1")]
fn try_from_v1_line_info_flags() {
assert_eq!(Bias::try_from(v1::LineInfoFlags::ACTIVE_LOW), Err(()));
assert_eq!(
Bias::try_from(v1::LineInfoFlags::BIAS_PULL_DOWN),
Ok(Bias::PullDown)
);
assert_eq!(
Bias::try_from(v1::LineInfoFlags::BIAS_PULL_UP),
Ok(Bias::PullUp)
);
assert_eq!(
Bias::try_from(v1::LineInfoFlags::BIAS_DISABLED),
Ok(Bias::Disabled)
);
}
#[test]
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
fn from_v2_line_flags() {
assert_eq!(Bias::try_from(v2::LineFlags::INPUT), Err(()));
assert_eq!(
Bias::try_from(v2::LineFlags::BIAS_PULL_DOWN),
Ok(Bias::PullDown)
);
assert_eq!(
Bias::try_from(v2::LineFlags::BIAS_PULL_UP),
Ok(Bias::PullUp)
);
assert_eq!(
Bias::try_from(v2::LineFlags::BIAS_DISABLED),
Ok(Bias::Disabled)
);
}
}
mod drive {
use super::*;
#[test]
fn default() {
assert_eq!(Drive::default(), Drive::PushPull);
}
#[test]
#[cfg(feature = "uapi_v1")]
fn try_from_v1_line_info_flags() {
assert_eq!(Drive::try_from(v1::LineInfoFlags::ACTIVE_LOW), Err(()));
assert_eq!(
Drive::try_from(v1::LineInfoFlags::OUTPUT),
Ok(Drive::PushPull)
);
assert_eq!(
Drive::try_from(v1::LineInfoFlags::OUTPUT | v1::LineInfoFlags::OPEN_DRAIN),
Ok(Drive::OpenDrain)
);
assert_eq!(
Drive::try_from(v1::LineInfoFlags::OUTPUT | v1::LineInfoFlags::OPEN_SOURCE),
Ok(Drive::OpenSource)
);
}
#[test]
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
fn try_from_v2_line_flags() {
assert_eq!(Drive::try_from(v2::LineFlags::INPUT), Err(()));
assert_eq!(Drive::try_from(v2::LineFlags::OUTPUT), Ok(Drive::PushPull));
assert_eq!(
Drive::try_from(v2::LineFlags::OUTPUT | v2::LineFlags::OPEN_DRAIN),
Ok(Drive::OpenDrain)
);
assert_eq!(
Drive::try_from(v2::LineFlags::OUTPUT | v2::LineFlags::OPEN_SOURCE),
Ok(Drive::OpenSource)
);
}
}
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
mod edge_detection {
use super::{v2, EdgeDetection};
#[test]
fn try_from_v2_line_flags() {
assert_eq!(EdgeDetection::try_from(v2::LineFlags::INPUT), Err(()));
assert_eq!(
EdgeDetection::try_from(v2::LineFlags::EDGE_RISING),
Ok(EdgeDetection::RisingEdge)
);
assert_eq!(
EdgeDetection::try_from(v2::LineFlags::EDGE_FALLING),
Ok(EdgeDetection::FallingEdge)
);
assert_eq!(
EdgeDetection::try_from(v2::LineFlags::EDGE_RISING | v2::LineFlags::EDGE_FALLING),
Ok(EdgeDetection::BothEdges)
);
}
}
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
mod event_clock {
use super::{v2, EventClock};
#[test]
fn default() {
assert_eq!(EventClock::default(), EventClock::Monotonic);
}
#[test]
fn from_v2_line_flags() {
assert_eq!(
EventClock::from(v2::LineFlags::INPUT),
EventClock::Monotonic
);
assert_eq!(
EventClock::from(v2::LineFlags::EVENT_CLOCK_REALTIME),
EventClock::Realtime
);
}
}
mod offset_hasher {
use super::OffsetHasher;
use std::hash::Hasher;
#[test]
fn write_u32() {
let mut h = OffsetHasher::default();
h.write_u32(2042);
assert_eq!(2042, h.finish());
}
#[test]
#[should_panic]
fn write() {
let mut h = OffsetHasher::default();
h.write(&[42]);
}
#[test]
#[should_panic]
fn write_u64() {
let mut h = OffsetHasher::default();
h.write_u64(2042);
}
#[test]
#[should_panic]
fn write_u8() {
let mut h = OffsetHasher::default();
h.write_u8(42);
}
}
}