use usb_device::UsbError;
use usb_device::class_prelude::UsbBus;
use usbd_serial::SerialPort;
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::Serial(event).into()
}
}
pub trait Api: Send {
fn read(output: &mut [u8]) -> Result<usize, Error>;
fn write(input: &[u8]) -> Result<usize, Error>;
fn flush() -> Result<(), Error>;
fn enable(event: &Event) -> Result<(), Error>;
fn disable(event: &Event) -> Result<(), Error>;
}
pub trait HasSerial: Send {
type UsbBus: UsbBus;
fn with_serial<R>(f: impl FnOnce(&mut Serial<Self::UsbBus>) -> R) -> R;
}
pub struct WithSerial<T: HasSerial> {
_never: !,
_has_serial: T,
}
pub struct Serial<'a, T: UsbBus> {
port: SerialPort<'a, T>,
read_enabled: bool,
write_enabled: bool,
}
impl<'a, T: UsbBus> Serial<'a, T> {
pub fn new(port: SerialPort<'a, T>) -> Self {
Self { port, read_enabled: false, write_enabled: false }
}
pub fn read(&mut self, output: &mut [u8]) -> Result<usize, Error> {
match self.port.read(output) {
Ok(len) => {
log::trace!("{}{:?} = read({})", len, &output[.. len], output.len());
Ok(len)
}
Err(UsbError::WouldBlock) => Ok(0),
Err(e) => {
log::debug!("{} = read({})", log::Debug2Format(&e), output.len());
Err(Error::world(0))
}
}
}
pub fn write(&mut self, input: &[u8]) -> Result<usize, Error> {
if !self.port.dtr() {
return Ok(0);
}
match self.port.write(input) {
Ok(len) => {
log::trace!("{} = write({}{:?})", len, input.len(), input);
Ok(len)
}
Err(UsbError::WouldBlock) => Ok(0),
Err(e) => {
log::debug!("{} = write({}{:?})", log::Debug2Format(&e), input.len(), input);
Err(Error::world(0))
}
}
}
pub fn flush(&mut self) -> Result<(), Error> {
loop {
match self.port.flush() {
Ok(()) => {
log::trace!("flush()");
break Ok(());
}
Err(UsbError::WouldBlock) => {
log::debug!("flush() didn't flush all data, retrying");
continue;
}
Err(e) => {
log::debug!("{} = flush()", log::Debug2Format(&e));
break Err(Error::world(0));
}
}
}
}
pub fn enable(&mut self, event: &Event) -> Result<(), Error> {
self.set(event, true)
}
pub fn disable(&mut self, event: &Event) -> Result<(), Error> {
self.set(event, false)
}
pub fn port(&mut self) -> &mut SerialPort<'a, T> {
&mut self.port
}
pub fn tick(&mut self, polled: bool, mut push: impl FnMut(Event)) {
if self.read_enabled && polled {
push(Event::Read);
}
if self.write_enabled && self.port.dtr() {
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: HasSerial> Api for WithSerial<T> {
fn read(output: &mut [u8]) -> Result<usize, Error> {
T::with_serial(|x| x.read(output))
}
fn write(input: &[u8]) -> Result<usize, Error> {
T::with_serial(|x| x.write(input))
}
fn flush() -> Result<(), Error> {
T::with_serial(|x| x.flush())
}
fn enable(event: &Event) -> Result<(), Error> {
T::with_serial(|x| x.enable(event))
}
fn disable(event: &Event) -> Result<(), Error> {
T::with_serial(|x| x.disable(event))
}
}