use crate::protocol::opcode::{CATCH_CTRL_NAK, CATCH_CTRL_OK, CATCH_CTRL_STALL};
use crate::types::{Axis, CatchClass, Class, ClockDomain, Direction, Usage};
pub(crate) const EVENT_HDR: usize = 5;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MotionEvent {
pub ts_us: u32,
pub clock: ClockDomain,
pub dx: i16,
pub dy: i16,
pub dz: i16,
}
impl MotionEvent {
pub(crate) fn from_payload(p: &[u8]) -> Option<MotionEvent> {
if p.len() < EVENT_HDR + 6 {
return None;
}
Some(MotionEvent {
ts_us: u32::from_le_bytes([p[0], p[1], p[2], p[3]]),
clock: ClockDomain::from_u8(p[4]),
dx: i16::from_le_bytes([p[5], p[6]]),
dy: i16::from_le_bytes([p[7], p[8]]),
dz: i16::from_le_bytes([p[9], p[10]]),
})
}
pub fn all_axes(&self) -> [(Axis, i16); 3] {
[
(Axis::X, self.dx),
(Axis::Y, self.dy),
(Axis::Wheel, self.dz),
]
}
pub fn axes(&self) -> impl Iterator<Item = (Axis, i16)> + use<> {
self.all_axes().into_iter().filter(|(_, d)| *d != 0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UsageSnapshot {
pub ts_us: u32,
pub clock: ClockDomain,
pub class: Class,
pub direction: Direction,
pub usages: Vec<Usage>,
}
impl UsageSnapshot {
pub(crate) fn from_payload(p: &[u8]) -> Option<UsageSnapshot> {
if p.len() < EVENT_HDR + 2 {
return None;
}
Some(UsageSnapshot {
ts_us: u32::from_le_bytes([p[0], p[1], p[2], p[3]]),
clock: ClockDomain::from_u8(p[4]),
class: Class::from_u8(p[5])?,
direction: Direction::from_u8(p[6])?,
usages: Usage::decode_list(&p[EVENT_HDR + 2..])?,
})
}
pub fn is_held(&self, usage: impl Into<Usage>) -> bool {
let u = usage.into();
self.usages.contains(&u)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BusEvent {
Reset,
Suspend,
Resume,
Configured(u8),
Deconfigured,
SetInterface { interface: u8, alt: u8 },
DeviceAttached,
DeviceDetached,
CloneUp,
CloneDown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ControlStatus {
Ok,
Stalled,
Naked,
Other(u8),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TrafficEvent {
pub ts_us: u32,
pub clock: ClockDomain,
pub class: CatchClass,
pub id: u16,
pub direction: Direction,
pub flags: u8,
pub true_len: u16,
pub bytes: Vec<u8>,
}
impl TrafficEvent {
pub(crate) fn from_payload(p: &[u8]) -> Option<TrafficEvent> {
if p.len() < 12 {
return None;
}
Some(TrafficEvent {
ts_us: u32::from_le_bytes([p[0], p[1], p[2], p[3]]),
clock: ClockDomain::from_u8(p[4]),
class: CatchClass::from_u8(p[5])?,
id: u16::from_le_bytes([p[6], p[7]]),
direction: Direction::from_u8(p[8])?,
flags: p[9],
true_len: u16::from_le_bytes([p[10], p[11]]),
bytes: p[12..].to_vec(),
})
}
pub fn truncated(&self) -> bool {
(self.bytes.len() as u16) < self.true_len
}
pub fn setup(&self) -> Option<&[u8]> {
if self.class == CatchClass::Control && self.bytes.len() >= 8 {
Some(&self.bytes[..8])
} else {
None
}
}
pub fn data(&self) -> &[u8] {
if self.class != CatchClass::Control {
return &self.bytes;
}
if self.bytes.len() >= 8 {
&self.bytes[8..]
} else {
&[]
}
}
pub fn control_status(&self) -> Option<ControlStatus> {
if self.class != CatchClass::Control {
return None;
}
Some(match self.flags {
CATCH_CTRL_OK => ControlStatus::Ok,
CATCH_CTRL_STALL => ControlStatus::Stalled,
CATCH_CTRL_NAK => ControlStatus::Naked,
v => ControlStatus::Other(v),
})
}
pub fn bus_event(&self) -> Option<BusEvent> {
if self.class != CatchClass::Bus {
return None;
}
let a = self.bytes.first().copied().unwrap_or(0);
let b = self.bytes.get(1).copied().unwrap_or(0);
Some(match self.flags {
0 => BusEvent::Reset,
1 => BusEvent::Suspend,
2 => BusEvent::Resume,
3 => BusEvent::Configured(a),
4 => BusEvent::Deconfigured,
5 => BusEvent::SetInterface {
interface: a,
alt: b,
},
6 => BusEvent::DeviceAttached,
7 => BusEvent::DeviceDetached,
8 => BusEvent::CloneUp,
9 => BusEvent::CloneDown,
_ => return None,
})
}
pub fn bulk_end_of_transfer(&self) -> bool {
self.class == CatchClass::VendorBulk && self.flags & 0x01 != 0
}
pub fn bulk_zlp(&self) -> bool {
self.class == CatchClass::VendorBulk && self.flags & 0x02 != 0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CatchEvent {
Motion(MotionEvent),
Usages(UsageSnapshot),
Traffic(TrafficEvent),
}
impl CatchEvent {
pub fn ts_us(&self) -> u32 {
match self {
CatchEvent::Motion(m) => m.ts_us,
CatchEvent::Usages(u) => u.ts_us,
CatchEvent::Traffic(t) => t.ts_us,
}
}
pub fn clock(&self) -> ClockDomain {
match self {
CatchEvent::Motion(m) => m.clock,
CatchEvent::Usages(u) => u.clock,
CatchEvent::Traffic(t) => t.clock,
}
}
pub fn class(&self) -> CatchClass {
match self {
CatchEvent::Motion(_) => CatchClass::Axis,
CatchEvent::Usages(u) => u.class.into(),
CatchEvent::Traffic(t) => t.class,
}
}
pub fn id(&self) -> Option<u16> {
match self {
CatchEvent::Motion(_) | CatchEvent::Usages(_) => None,
CatchEvent::Traffic(t) => Some(t.id),
}
}
pub fn direction(&self) -> Direction {
match self {
CatchEvent::Motion(_) => Direction::Both,
CatchEvent::Usages(u) => u.direction,
CatchEvent::Traffic(t) => t.direction,
}
}
pub fn bytes(&self) -> &[u8] {
match self {
CatchEvent::Motion(_) | CatchEvent::Usages(_) => &[],
CatchEvent::Traffic(t) => &t.bytes,
}
}
}