use static_cell::StaticCell;
use usb_device::device::UsbDeviceState;
const VID_PID: usb_device::device::UsbVidPid = usb_device::device::UsbVidPid(0x5824, 0x27dd);
const PRODUCT: &str = "imxrt-log";
const ENDPOINT_BYTES: usize = MAX_PACKET_SIZE * 2 + EP0_CONTROL_PACKET_SIZE * 2 + 128;
static ENDPOINT_MEMORY: imxrt_usbd::EndpointMemory<ENDPOINT_BYTES> =
imxrt_usbd::EndpointMemory::new();
static ENDPOINT_STATE: imxrt_usbd::EndpointState<6> = imxrt_usbd::EndpointState::new();
type Bus = imxrt_usbd::BusAdapter;
type BusAllocator = usb_device::bus::UsbBusAllocator<Bus>;
type Class<'a> = usbd_serial::CdcAcmClass<'a, Bus>;
type Device<'a> = usb_device::device::UsbDevice<'a, Bus>;
const MAX_PACKET_SIZE: usize = crate::config::USB_BULK_MPS;
const EP0_CONTROL_PACKET_SIZE: usize = 64;
const GPT_INSTANCE: imxrt_usbd::gpt::Instance = imxrt_usbd::gpt::Instance::Gpt0;
pub(crate) struct Backend {
class: Class<'static>,
device: Device<'static>,
consumer: crate::Consumer,
configured: bool,
}
impl Backend {
pub(crate) fn poll(&mut self) {
let class_event = self.device.poll(&mut [&mut self.class]);
let timer_event = self.device.bus().gpt_mut(GPT_INSTANCE, |gpt| {
let mut elapsed = false;
while gpt.is_elapsed() {
gpt.clear_elapsed();
elapsed = true;
}
elapsed || !gpt.is_running()
});
let check_consumer = class_event || timer_event;
if self.device.state() != UsbDeviceState::Configured {
if self.configured {
self.device.bus().gpt_mut(GPT_INSTANCE, |gpt| gpt.stop());
}
self.configured = false;
return;
}
if !self.configured {
self.device.bus().configure();
self.device.bus().gpt_mut(GPT_INSTANCE, |gpt| {
if gpt.is_interrupt_enabled() {
gpt.run()
}
});
self.configured = true;
}
self.class.read_packet(&mut []).ok();
if check_consumer && let Ok(grant) = self.consumer.read() {
let buf = grant.buf();
if let Ok(written) = self
.class
.write_packet(&buf[..MAX_PACKET_SIZE.min(buf.len())])
{
grant.release(written);
}
} }
}
pub(crate) fn init<const N: u8>(
peripherals: imxrt_usbd::Instances<N>,
interrupts: crate::Interrupts,
consumer: super::Consumer,
config: &UsbdConfig,
) -> &'static mut Backend {
static BACKEND: StaticCell<Backend> = StaticCell::new();
BACKEND.init_with(|| {
static BUS: StaticCell<BusAllocator> = StaticCell::new();
let bus = BUS.init_with(|| {
let bus = unsafe {
imxrt_usbd::BusAdapter::without_critical_sections(
peripherals,
&ENDPOINT_MEMORY,
&ENDPOINT_STATE,
crate::config::USB_SPEED,
)
};
bus.set_interrupts(interrupts == crate::Interrupts::Enabled);
bus.gpt_mut(GPT_INSTANCE, |gpt| {
gpt.stop();
gpt.clear_elapsed();
gpt.set_interrupt_enabled(interrupts == crate::Interrupts::Enabled);
gpt.set_mode(imxrt_usbd::gpt::Mode::Repeat);
gpt.set_load(config.poll_interval_us);
gpt.reset();
});
usb_device::bus::UsbBusAllocator::new(bus)
});
let class = usbd_serial::CdcAcmClass::new(bus, MAX_PACKET_SIZE as u16);
let device = usb_device::device::UsbDeviceBuilder::new(bus, VID_PID)
.strings(&[usb_device::device::StringDescriptors::default().product(PRODUCT)])
.unwrap()
.device_class(usbd_serial::USB_CLASS_CDC)
.max_packet_size_0(EP0_CONTROL_PACKET_SIZE as u8)
.unwrap()
.build();
for idx in 1..8 {
for dir in &[usb_device::UsbDirection::In, usb_device::UsbDirection::Out] {
let ep_addr = usb_device::endpoint::EndpointAddress::from_parts(idx, *dir);
device.bus().enable_zlt(ep_addr);
}
}
Backend {
class,
device,
consumer,
configured: false,
}
})
}
#[derive(PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UsbdConfigBuilder {
cfg: UsbdConfig,
}
impl Default for UsbdConfigBuilder {
fn default() -> Self {
Self::new()
}
}
impl UsbdConfigBuilder {
pub const fn new() -> Self {
Self {
cfg: UsbdConfig::default(),
}
}
pub const fn build(self) -> UsbdConfig {
self.cfg
}
pub const fn poll_interval_us(mut self, poll_interval_us: u32) -> Self {
self.cfg.poll_interval_us = poll_interval_us;
self
}
}
#[derive(PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UsbdConfig {
poll_interval_us: u32,
}
impl UsbdConfig {
const fn default() -> Self {
Self {
poll_interval_us: 4_000,
}
}
}