use core::{marker::PhantomData, sync::atomic::AtomicU8};
use bbq2::{
queue::BBQueue,
traits::{bbqhdl::BbqHandle, notifier::maitake::MaiNotSpsc, storage::Inline},
};
use static_cell::ConstStaticCell;
use crate::interface_manager::{Interface, utils::framed_stream};
pub struct EmbassyInterface<Q: BbqHandle + 'static> {
_pd: PhantomData<Q>,
}
struct ErgotHandler {}
pub type Queue<const N: usize, C> = BBQueue<Inline<N>, C, MaiNotSpsc>;
pub type EmbassySink<Q> = framed_stream::Sink<Q>;
impl<Q: BbqHandle + 'static> Interface for EmbassyInterface<Q> {
type Sink = EmbassySink<Q>;
}
enum TransmitError {
ConnectionClosed,
Timeout,
}
pub const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
pub const DEFAULT_TIMEOUT_MS_PER_FRAME: usize = 2;
pub const USB_FS_MAX_PACKET_SIZE: usize = 64;
static STINDX: AtomicU8 = AtomicU8::new(0xFF);
static HDLR: ConstStaticCell<ErgotHandler> = ConstStaticCell::new(ErgotHandler {});
pub struct UsbDeviceBuffers<
const CONFIG: usize = 256,
const BOS: usize = 256,
const CONTROL: usize = 64,
const MSOS: usize = 256,
> {
pub config_descriptor: [u8; CONFIG],
pub bos_descriptor: [u8; BOS],
pub control_buf: [u8; CONTROL],
pub msos_descriptor: [u8; MSOS],
}
impl<const CONFIG: usize, const BOS: usize, const CONTROL: usize, const MSOS: usize>
UsbDeviceBuffers<CONFIG, BOS, CONTROL, MSOS>
{
pub const fn new() -> Self {
Self {
config_descriptor: [0u8; CONFIG],
bos_descriptor: [0u8; BOS],
msos_descriptor: [0u8; MSOS],
control_buf: [0u8; CONTROL],
}
}
}
impl<const CONFIG: usize, const BOS: usize, const CONTROL: usize, const MSOS: usize> Default
for UsbDeviceBuffers<CONFIG, BOS, CONTROL, MSOS>
{
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "embassy-usb-v0_5")]
pub mod eusb_0_5 {
use core::sync::atomic::Ordering;
use crate::logging::{info, warn};
use bbq2::{
prod_cons::framed::FramedConsumer,
queue::BBQueue,
traits::{coordination::Coord, notifier::maitake::MaiNotSpsc, storage::Inline},
};
use embassy_futures::select::{Either, select};
use embassy_time::Timer;
use embassy_usb_0_5::{
Builder, UsbDevice,
driver::{Driver, Endpoint, EndpointIn},
msos::{self, windows_version},
};
use static_cell::ConstStaticCell;
use crate::interface_manager::interface_impls::embassy_usb::TransmitError;
use super::{DEVICE_INTERFACE_GUIDS, ErgotHandler, HDLR, STINDX, UsbDeviceBuffers};
pub struct WireStorage<
const CONFIG: usize = 256,
const BOS: usize = 256,
const CONTROL: usize = 64,
const MSOS: usize = 256,
> {
pub bufs_usb: ConstStaticCell<UsbDeviceBuffers<CONFIG, BOS, CONTROL, MSOS>>,
}
pub async fn tx_worker<'a, D: Driver<'a>, const N: usize, C: Coord>(
ep_in: &mut D::EndpointIn,
rx: FramedConsumer<&'static BBQueue<Inline<N>, C, MaiNotSpsc>>,
timeout_ms_per_frame: usize,
max_usb_frame_size: usize,
) {
assert!(max_usb_frame_size.is_power_of_two());
info!("Started tx_worker");
let mut pending = false;
loop {
ep_in.wait_enabled().await;
info!("Endpoint marked connected");
'connection: loop {
let frame = rx.wait_read().await;
defmt::debug!("Got frame to send len {=usize}", frame.len());
let res = send_all::<D>(
ep_in,
&frame,
&mut pending,
timeout_ms_per_frame,
max_usb_frame_size,
)
.await;
frame.release();
match res {
Ok(()) => {}
Err(TransmitError::Timeout) => {
warn!("Transmit timeout!");
}
Err(TransmitError::ConnectionClosed) => break 'connection,
}
}
while let Ok(frame) = rx.read() {
frame.release();
}
}
}
#[inline]
async fn send_all<'a, D>(
ep_in: &mut D::EndpointIn,
out: &[u8],
pending_frame: &mut bool,
timeout_ms_per_frame: usize,
max_usb_frame_size: usize,
) -> Result<(), TransmitError>
where
D: Driver<'a>,
{
if out.is_empty() {
return Ok(());
}
let frames = out.len().div_ceil(max_usb_frame_size);
let timeout_ms = frames * timeout_ms_per_frame;
let send_fut = async {
if *pending_frame && ep_in.write(&[]).await.is_err() {
return Err(TransmitError::ConnectionClosed);
}
*pending_frame = true;
for ch in out.chunks(max_usb_frame_size) {
if ep_in.write(ch).await.is_err() {
return Err(TransmitError::ConnectionClosed);
}
}
if (out.len() & (max_usb_frame_size - 1)) == 0 && ep_in.write(&[]).await.is_err() {
return Err(TransmitError::ConnectionClosed);
}
*pending_frame = false;
Ok(())
};
match select(send_fut, Timer::after_millis(timeout_ms as u64)).await {
Either::First(res) => res,
Either::Second(()) => Err(TransmitError::Timeout),
}
}
impl<const CONFIG: usize, const BOS: usize, const CONTROL: usize, const MSOS: usize>
WireStorage<CONFIG, BOS, CONTROL, MSOS>
{
pub const fn new() -> Self {
Self {
bufs_usb: ConstStaticCell::new(UsbDeviceBuffers::new()),
}
}
pub fn init_ergot<D: Driver<'static> + 'static>(
&'static self,
driver: D,
config: embassy_usb_0_5::Config<'static>,
) -> (UsbDevice<'static, D>, D::EndpointIn, D::EndpointOut) {
let bufs = self.bufs_usb.take();
let mut builder = Builder::new(
driver,
config,
&mut bufs.config_descriptor,
&mut bufs.bos_descriptor,
&mut bufs.msos_descriptor,
&mut bufs.control_buf,
);
let hdlr = HDLR.take();
builder.handler(hdlr);
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 stindx = interface.string();
STINDX.store(stindx.0, Ordering::Relaxed);
let mut alt = interface.alt_setting(0xFF, 0xCA, 0x7D, Some(stindx));
let ep_out = alt.endpoint_bulk_out(None, 64);
let ep_in = alt.endpoint_bulk_in(None, 64);
drop(function);
let usb = builder.build();
(usb, ep_in, ep_out)
}
pub fn init<D: Driver<'static> + 'static>(
&'static self,
driver: D,
config: embassy_usb_0_5::Config<'static>,
) -> (UsbDevice<'static, D>, D::EndpointIn, D::EndpointOut) {
let (builder, wtx, wrx) = self.init_without_build(driver, config);
let usb = builder.build();
(usb, wtx, wrx)
}
pub fn init_without_build<D: Driver<'static> + 'static>(
&'static self,
driver: D,
config: embassy_usb_0_5::Config<'static>,
) -> (Builder<'static, D>, D::EndpointIn, D::EndpointOut) {
let bufs = self.bufs_usb.take();
let mut builder = Builder::new(
driver,
config,
&mut bufs.config_descriptor,
&mut bufs.bos_descriptor,
&mut bufs.msos_descriptor,
&mut bufs.control_buf,
);
let hdlr = HDLR.take();
builder.handler(hdlr);
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 stindx = interface.string();
STINDX.store(stindx.0, Ordering::Relaxed);
let mut alt = interface.alt_setting(0xFF, 0xCA, 0x7D, Some(stindx));
let ep_out = alt.endpoint_bulk_out(None, 64);
let ep_in = alt.endpoint_bulk_in(None, 64);
drop(function);
(builder, ep_in, ep_out)
}
}
impl<const CONFIG: usize, const BOS: usize, const CONTROL: usize, const MSOS: usize> Default
for WireStorage<CONFIG, BOS, CONTROL, MSOS>
{
fn default() -> Self {
Self::new()
}
}
impl embassy_usb_0_5::Handler for ErgotHandler {
fn get_string(
&mut self,
index: embassy_usb_0_5::types::StringIndex,
lang_id: u16,
) -> Option<&str> {
use embassy_usb_0_5::descriptor::lang_id;
let stindx = STINDX.load(Ordering::Relaxed);
if stindx == 0xFF {
return None;
}
if lang_id == lang_id::ENGLISH_US && index.0 == stindx {
Some("ergot")
} else {
None
}
}
}
}