use usb_device::bus::UsbBus;
use usbd_hid::UsbError;
use usbd_hid::hid_class::HIDClass;
use wasefire_error::Code;
use wasefire_logger as log;
use crate::Error;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq)]
pub enum Event {
Read,
Write,
}
impl<B: crate::Api> From<Event> for crate::Event<B> {
fn from(event: Event) -> Self {
super::Event::Ctap(event).into()
}
}
pub trait Api: Send {
fn read(output: &mut [u8; 64]) -> Result<bool, Error>;
fn write(input: &[u8; 64]) -> Result<bool, Error>;
fn enable(event: &Event) -> Result<(), Error>;
fn disable(event: &Event) -> Result<(), Error>;
}
pub trait HasHid: Send {
type UsbBus: UsbBus;
fn with_hid<R>(f: impl FnOnce(&mut Ctap<Self::UsbBus>) -> R) -> R;
}
pub struct WithHid<T: HasHid> {
_never: !,
_has_hid: T,
}
pub struct Ctap<'a, T: UsbBus> {
class: HIDClass<'a, T>,
read_enabled: bool,
write_enabled: bool,
}
impl<'a, T: UsbBus> Ctap<'a, T> {
pub fn new(class: HIDClass<'a, T>) -> Self {
Self { class, read_enabled: false, write_enabled: false }
}
pub fn class(&mut self) -> &mut HIDClass<'a, T> {
&mut self.class
}
pub fn tick(&mut self, polled: bool, mut push: impl FnMut(Event)) {
if self.read_enabled && polled {
push(Event::Read);
}
if self.write_enabled {
push(Event::Write);
}
}
fn set(&mut self, event: &Event, enabled: bool) -> Result<(), Error> {
match event {
Event::Read => self.read_enabled = enabled,
Event::Write => self.write_enabled = enabled,
}
Ok(())
}
}
impl<T: HasHid> Api for WithHid<T> {
fn read(output: &mut [u8; 64]) -> Result<bool, Error> {
T::with_hid(|x| match x.class.pull_raw_output(output) {
Ok(64) => Ok(true),
Ok(len) => {
log::warn!("bad read len {}", len);
Err(Error::world(Code::InvalidLength))
}
Err(UsbError::WouldBlock) => Ok(false),
Err(_) => Err(Error::world(0)),
})
}
fn write(input: &[u8; 64]) -> Result<bool, Error> {
T::with_hid(|x| match x.class.push_raw_input(input) {
Ok(64) => Ok(true),
Ok(len) => {
log::warn!("bad write len {}", len);
Err(Error::world(Code::InvalidLength))
}
Err(UsbError::WouldBlock) => Ok(false),
Err(_) => Err(Error::world(0)),
})
}
fn enable(event: &Event) -> Result<(), Error> {
T::with_hid(|x| x.set(event, true))
}
fn disable(event: &Event) -> Result<(), Error> {
T::with_hid(|x| x.set(event, false))
}
}