use super::{Info, Offset};
use crate::{Error, UapiField};
#[cfg(all(feature = "uapi_v1", 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};
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EdgeEvent {
pub timestamp_ns: u64,
pub kind: EdgeKind,
pub offset: Offset,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_zero"))]
pub seqno: u32,
#[cfg_attr(
feature = "serde",
serde(rename = "lineSeqno", skip_serializing_if = "is_zero")
)]
pub line_seqno: u32,
}
#[cfg(feature = "uapi_v1")]
impl TryFrom<&v1::LineEdgeEvent> for EdgeEvent {
type Error = Error;
fn try_from(le: &v1::LineEdgeEvent) -> Result<Self, Self::Error> {
let kind = uapi::LineEdgeEventKind::try_from(le.kind)
.map_err(|_| Error::UnexpectedResponse(UapiField::Kind, format!("{}", le.kind)))?;
Ok(EdgeEvent {
timestamp_ns: le.timestamp_ns,
kind: kind.into(),
offset: 0,
seqno: 0,
line_seqno: 0,
})
}
}
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
impl TryFrom<&v2::LineEdgeEvent> for EdgeEvent {
type Error = Error;
fn try_from(le: &v2::LineEdgeEvent) -> Result<Self, Self::Error> {
let kind = uapi::LineEdgeEventKind::try_from(le.kind)
.map_err(|_| Error::UnexpectedResponse(UapiField::Kind, format!("{}", le.kind)))?;
Ok(EdgeEvent {
timestamp_ns: le.timestamp_ns,
kind: kind.into(),
offset: le.offset,
seqno: le.seqno,
line_seqno: le.line_seqno,
})
}
}
#[cfg(feature = "serde")]
fn is_zero(u: &u32) -> bool {
*u == 0
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum EdgeKind {
Rising = 1,
Falling = 2,
}
impl From<uapi::LineEdgeEventKind> for EdgeKind {
fn from(kind: uapi::LineEdgeEventKind) -> Self {
match kind {
uapi::LineEdgeEventKind::RisingEdge => EdgeKind::Rising,
uapi::LineEdgeEventKind::FallingEdge => EdgeKind::Falling,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct InfoChangeEvent {
#[cfg_attr(feature = "serde", serde(flatten))]
pub info: Info,
pub timestamp_ns: u64,
pub kind: InfoChangeKind,
}
#[cfg(feature = "uapi_v1")]
impl TryFrom<&v1::LineInfoChangeEvent> for InfoChangeEvent {
type Error = Error;
fn try_from(ice: &v1::LineInfoChangeEvent) -> Result<Self, Self::Error> {
let kind = InfoChangeKind::try_from(ice.kind)
.map_err(|_| Error::UnexpectedResponse(UapiField::Kind, format!("{}", ice.kind)))?;
Ok(InfoChangeEvent {
info: Info::from(&ice.info),
timestamp_ns: ice.timestamp_ns,
kind,
})
}
}
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
impl TryFrom<&v2::LineInfoChangeEvent> for InfoChangeEvent {
type Error = Error;
fn try_from(ice: &v2::LineInfoChangeEvent) -> Result<Self, Self::Error> {
let kind = InfoChangeKind::try_from(ice.kind)
.map_err(|_| Error::UnexpectedResponse(UapiField::Kind, format!("{}", ice.kind)))?;
Ok(InfoChangeEvent {
info: Info::try_from(&ice.info)?,
timestamp_ns: ice.timestamp_ns,
kind,
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum InfoChangeKind {
Requested = 1,
Released = 2,
Reconfigured = 3,
}
impl TryFrom<u32> for InfoChangeKind {
type Error = ();
fn try_from(kind: u32) -> Result<Self, Self::Error> {
Ok(match kind {
1 => InfoChangeKind::Requested,
2 => InfoChangeKind::Released,
3 => InfoChangeKind::Reconfigured,
_ => return Err(()),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::line::Drive;
mod edge_event {
use super::*;
#[test]
#[cfg(feature = "uapi_v1")]
fn try_from_v1() {
let mut v1event = v1::LineEdgeEvent {
timestamp_ns: 1234,
kind: gpiocdev_uapi::v1::LineEdgeEventKind::FallingEdge as u32,
};
let ee = EdgeEvent::try_from(&v1event).unwrap();
assert_eq!(ee.timestamp_ns, 1234);
assert_eq!(ee.kind, EdgeKind::Falling);
assert_eq!(ee.offset, 0);
assert_eq!(ee.seqno, 0);
assert_eq!(ee.line_seqno, 0);
v1event.kind = 42;
let ee = EdgeEvent::try_from(&v1event).unwrap_err();
assert_eq!(ee, Error::UnexpectedResponse(UapiField::Kind, "42".into()));
}
#[test]
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
fn try_from_v2() {
let mut v2event = v2::LineEdgeEvent {
timestamp_ns: 1234,
kind: gpiocdev_uapi::v2::LineEdgeEventKind::RisingEdge as u32,
offset: 23,
seqno: 2,
line_seqno: 1,
padding: Default::default(),
};
let ee = EdgeEvent::try_from(&v2event).unwrap();
assert_eq!(ee.timestamp_ns, 1234);
assert_eq!(ee.kind, EdgeKind::Rising);
assert_eq!(ee.offset, 23);
assert_eq!(ee.seqno, 2);
assert_eq!(ee.line_seqno, 1);
v2event.kind = 42;
let ee = EdgeEvent::try_from(&v2event).unwrap_err();
assert_eq!(ee, Error::UnexpectedResponse(UapiField::Kind, "42".into()));
}
}
mod info_change_event {
use super::*;
#[test]
#[cfg(feature = "uapi_v1")]
fn try_from_v1() {
let mut v1event = v1::LineInfoChangeEvent {
timestamp_ns: 1234,
kind: gpiocdev_uapi::v1::LineInfoChangeKind::Reconfigured as u32,
info: v1::LineInfo {
offset: 32,
flags: v1::LineInfoFlags::OPEN_DRAIN,
name: Default::default(),
consumer: Default::default(),
},
padding: Default::default(),
};
let ee = InfoChangeEvent::try_from(&v1event).unwrap();
assert_eq!(ee.timestamp_ns, 1234);
assert_eq!(ee.kind, InfoChangeKind::Reconfigured);
assert_eq!(ee.info.offset, 32);
assert_eq!(ee.info.drive, Some(Drive::OpenDrain));
v1event.kind = 42;
let ee = InfoChangeEvent::try_from(&v1event).unwrap_err();
assert_eq!(ee, Error::UnexpectedResponse(UapiField::Kind, "42".into()));
}
#[test]
#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
fn try_from_v2() {
let mut v2event = v2::LineInfoChangeEvent {
timestamp_ns: 1234,
kind: gpiocdev_uapi::v2::LineInfoChangeKind::Reconfigured as u32,
info: v2::LineInfo {
offset: 32,
flags: v2::LineFlags::OPEN_DRAIN,
name: Default::default(),
consumer: Default::default(),
num_attrs: 0,
attrs: Default::default(),
padding: Default::default(),
},
padding: Default::default(),
};
let ee = InfoChangeEvent::try_from(&v2event).unwrap();
assert_eq!(ee.timestamp_ns, 1234);
assert_eq!(ee.kind, InfoChangeKind::Reconfigured);
assert_eq!(ee.info.offset, 32);
assert_eq!(ee.info.drive, Some(Drive::OpenDrain));
v2event.kind = 42;
let ee = InfoChangeEvent::try_from(&v2event).unwrap_err();
assert_eq!(ee, Error::UnexpectedResponse(UapiField::Kind, "42".into()));
}
}
}