use core::{marker::PhantomData, sync::atomic::Ordering, task::Poll};
use embassy_usb_driver::{
Endpoint as DriverEndpoint, EndpointError, EndpointIn as DriverEndpointIn, EndpointInfo,
EndpointOut as DriverEndpointOut,
};
use crate::{
pac::usbfs::vals::{CfifoselCurpipe, PipectrPid},
usb::{Instance as UsbInstance, STATE},
};
pub struct EndpointIn<'a, I: UsbInstance> {
pub(crate) _phantom: PhantomData<&'a I>,
pub(crate) info: EndpointInfo,
pub(crate) pipe: u8,
}
pub struct EndpointOut<'a, I: UsbInstance> {
pub(crate) _phantom: PhantomData<&'a I>,
pub(crate) info: EndpointInfo,
pub(crate) pipe: u8,
}
impl<'a, I: UsbInstance> DriverEndpoint for EndpointIn<'a, I> {
fn info(&self) -> &EndpointInfo {
&self.info
}
async fn wait_enabled(&mut self) {
let pipe = self.pipe as usize;
core::future::poll_fn(|cx| {
STATE.ep_wakers[pipe].register(cx.waker());
if STATE.pipe_enabled.load(Ordering::Acquire) & (1 << pipe) != 0 {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
}
}
impl<'a, I: UsbInstance> DriverEndpointIn for EndpointIn<'a, I> {
async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> {
trace!(
"USB: EP{}IN write {} bytes",
self.info.addr.index(),
buf.len()
);
let r = I::regs();
let pipe = self.pipe as usize;
let pipectr_idx = pipe - 1;
if STATE.pipe_enabled.load(Ordering::Acquire) & (1 << pipe) == 0 {
return Err(EndpointError::Disabled);
}
if buf.len() > self.info.max_packet_size as _ {
return Err(EndpointError::BufferOverflow);
}
core::future::poll_fn(|cx| {
STATE.ep_wakers[pipe].register(cx.waker());
if STATE.pipe_enabled.load(Ordering::Acquire) & (1 << pipe) == 0 {
return Poll::Ready(Err(EndpointError::Disabled));
}
if STATE.pipe_bemp.load(Ordering::Acquire) & (1 << pipe) != 0 {
STATE.pipe_bemp.fetch_and(!(1 << pipe), Ordering::AcqRel);
Poll::Ready(Ok(()))
} else {
r.pipectr(pipectr_idx)
.modify(|r| r.set_pid(PipectrPid::Buffer));
Poll::Pending
}
})
.await?;
r.cfifosel()
.modify(|r| r.set_curpipe(CfifoselCurpipe::from_bits(self.pipe)));
while !r.cfifoctr().read().frdy() {
cortex_m::asm::nop();
}
for &byte in buf {
r.cfifol().write(|w| w.set_fifoport(byte));
}
r.cfifoctr().modify(|r| r.set_bval(true));
r.pipectr(pipectr_idx)
.modify(|r| r.set_pid(PipectrPid::Buffer));
Ok(())
}
}
impl<'a, I: UsbInstance> DriverEndpoint for EndpointOut<'a, I> {
fn info(&self) -> &EndpointInfo {
&self.info
}
async fn wait_enabled(&mut self) {
let pipe = self.pipe as usize;
core::future::poll_fn(|cx| {
STATE.ep_wakers[pipe].register(cx.waker());
if STATE.pipe_enabled.load(Ordering::Acquire) & (1 << pipe) != 0 {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
}
}
impl<'a, I: UsbInstance> DriverEndpointOut for EndpointOut<'a, I> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
trace!("USB: EP{}OUT read waiting", self.info.addr.index());
let r = I::regs();
let pipe = self.pipe as usize;
let pipectr_idx = pipe - 1;
if STATE.pipe_enabled.load(Ordering::Acquire) & (1 << pipe) == 0 {
return Err(EndpointError::Disabled);
}
core::future::poll_fn(|cx| {
STATE.ep_wakers[pipe].register(cx.waker());
if STATE.pipe_enabled.load(Ordering::Acquire) & (1 << pipe) == 0 {
return Poll::Ready(Err(EndpointError::Disabled));
}
if STATE.pipe_brdy.load(Ordering::Acquire) & (1 << pipe) != 0 {
STATE.pipe_brdy.fetch_and(!(1 << pipe), Ordering::AcqRel);
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
})
.await?;
r.cfifosel()
.modify(|r| r.set_curpipe(CfifoselCurpipe::from_bits(self.pipe)));
while !r.cfifoctr().read().frdy() {
cortex_m::asm::nop();
}
let data_len = r.cfifoctr().read().dtln() as usize;
if data_len > buf.len() {
r.cfifoctr().modify(|r| r.set_bclr(true));
r.pipectr(pipectr_idx)
.modify(|r| r.set_pid(PipectrPid::Buffer));
return Err(EndpointError::BufferOverflow);
}
for byte in buf[..data_len].iter_mut() {
*byte = r.cfifol().read().fifoport();
}
r.cfifoctr().modify(|r| r.set_bclr(true));
r.pipectr(pipectr_idx)
.modify(|r| r.set_pid(PipectrPid::Buffer));
trace!(
"USB: EP{}OUT read {} bytes",
self.info.addr.index(),
data_len
);
Ok(data_len)
}
}