#![allow(async_fn_in_trait)]
use crate::{
headered::extract_header_from_bytes,
standard_icd::{FrameTooLong, FrameTooShort, WireError},
WireHeader,
};
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_usb::{
driver::Driver,
msos::{self, windows_version},
Builder, UsbDevice,
};
use embassy_usb_driver::{Endpoint, EndpointError, EndpointOut};
use sender::Sender;
pub mod buffers;
pub mod dispatch_macro;
pub mod sender;
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
pub trait Dispatch {
type Mutex: RawMutex;
type Driver: Driver<'static>;
async fn dispatch(&mut self, hdr: WireHeader, body: &[u8]);
async fn error(&self, seq_no: u32, error: WireError);
fn sender(&self) -> Sender<Self::Mutex, Self::Driver>;
}
pub trait SpawnContext {
type SpawnCtxt: 'static;
fn spawn_ctxt(&mut self) -> Self::SpawnCtxt;
}
pub fn example_config() -> embassy_usb::Config<'static> {
let mut config = embassy_usb::Config::new(0x16c0, 0x27DD);
config.manufacturer = Some("Embassy");
config.product = Some("postcard-rpc example");
config.serial_number = Some("12345678");
config.device_class = 0xEF;
config.device_sub_class = 0x02;
config.device_protocol = 0x01;
config.composite_with_iads = true;
config
}
pub fn configure_usb<D: embassy_usb::driver::Driver<'static>>(
driver: D,
bufs: &'static mut buffers::UsbDeviceBuffers,
config: embassy_usb::Config<'static>,
) -> (UsbDevice<'static, D>, D::EndpointIn, D::EndpointOut) {
let mut builder = Builder::new(
driver,
config,
&mut bufs.config_descriptor,
&mut bufs.bos_descriptor,
&mut bufs.msos_descriptor,
&mut bufs.control_buf,
);
builder.msos_descriptor(windows_version::WIN8_1, 0);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
let mut function = builder.function(0xFF, 0, 0);
let mut interface = function.interface();
let mut alt = interface.alt_setting(0xFF, 0, 0, None);
let ep_out = alt.endpoint_bulk_out(64);
let ep_in = alt.endpoint_bulk_in(64);
drop(function);
let usb = builder.build();
(usb, ep_in, ep_out)
}
pub async fn rpc_dispatch<M, D, T>(
mut ep_out: D::EndpointOut,
mut dispatch: T,
rx_buf: &'static mut [u8],
) -> !
where
M: RawMutex + 'static,
D: Driver<'static> + 'static,
T: Dispatch<Mutex = M, Driver = D>,
{
'connect: loop {
ep_out.wait_enabled().await;
'packet: loop {
let mut window = &mut rx_buf[..];
'buffer: loop {
if window.is_empty() {
#[cfg(feature = "defmt")]
defmt::warn!("Overflow!");
let mut bonus: usize = 0;
loop {
match ep_out.read(rx_buf).await {
Ok(n) if n < 64 => {
bonus = bonus.saturating_add(n);
let err = WireError::FrameTooLong(FrameTooLong {
len: u32::try_from(bonus.saturating_add(rx_buf.len()))
.unwrap_or(u32::MAX),
max: u32::try_from(rx_buf.len()).unwrap_or(u32::MAX),
});
dispatch.error(0, err).await;
continue 'packet;
}
Ok(n) => {
bonus = bonus.saturating_add(n);
}
Err(EndpointError::BufferOverflow) => panic!(),
Err(EndpointError::Disabled) => continue 'connect,
};
}
}
let n = match ep_out.read(window).await {
Ok(n) => n,
Err(EndpointError::BufferOverflow) => panic!(),
Err(EndpointError::Disabled) => continue 'connect,
};
let (_now, later) = window.split_at_mut(n);
window = later;
if n != 64 {
break 'buffer;
}
}
let wlen = window.len();
let len = rx_buf.len() - wlen;
let frame = &rx_buf[..len];
#[cfg(feature = "defmt")]
defmt::debug!("got frame: {=usize}", frame.len());
if let Ok((hdr, body)) = extract_header_from_bytes(frame) {
dispatch.dispatch(hdr, body).await;
} else {
let err = WireError::FrameTooShort(FrameTooShort {
len: u32::try_from(frame.len()).unwrap_or(u32::MAX),
});
dispatch.error(0, err).await;
}
}
}
}