use core::{marker::PhantomData, sync::atomic::Ordering, task::Poll};
use embassy_usb_driver::{ControlPipe as DriverControlPipe, EndpointError};
use crate::{
pac::usbfs::vals::{CfifoselCurpipe, DcpctrPid},
usb::{Instance, STATE},
};
pub struct ControlPipe<'a, I: Instance> {
pub(crate) _phantom: PhantomData<&'a I>,
pub(crate) max_packet_size: u16,
}
impl<'a, I: Instance> DriverControlPipe for ControlPipe<'a, I> {
fn max_packet_size(&self) -> usize {
self.max_packet_size as _
}
async fn setup(&mut self) -> [u8; 8] {
let pkt = core::future::poll_fn(|cx| {
STATE.ep_wakers[0].register(cx.waker());
if STATE.setup_ready.load(Ordering::Acquire) {
STATE.setup_ready.store(false, Ordering::Release);
let lo = STATE.setup_lo.load(Ordering::Acquire);
let hi = STATE.setup_hi.load(Ordering::Acquire);
let pkt = [
lo as _,
(lo >> 8) as _,
(lo >> 16) as _,
(lo >> 24) as _,
hi as _,
(hi >> 8) as _,
(hi >> 16) as _,
(hi >> 24) as _,
];
Poll::Ready(pkt)
} else {
Poll::Pending
}
})
.await;
debug!(
"USB: setup [{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}]",
pkt[0], pkt[1], pkt[2], pkt[3], pkt[4], pkt[5], pkt[6], pkt[7]
);
pkt
}
async fn data_out(
&mut self,
buf: &mut [u8],
_first: bool,
_last: bool,
) -> Result<usize, EndpointError> {
trace!("USB: ctrl data_out waiting (buf={})", buf.len());
let r = I::regs();
r.cfifosel().modify(|r| {
r.set_curpipe(CfifoselCurpipe::Default);
r.set_isel(false);
});
r.dcpctr().modify(|r| r.set_pid(DcpctrPid::Buffer));
core::future::poll_fn(|cx| {
STATE.ep_wakers[0].register(cx.waker());
if STATE.pipe_brdy.load(Ordering::Acquire) & 1 != 0 {
STATE.pipe_brdy.fetch_and(!1, Ordering::AcqRel);
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
while !r.cfifoctr().read().frdy() {
cortex_m::asm::nop();
}
let data_len = r.cfifoctr().read().dtln() as usize;
let read_len = data_len.min(buf.len());
if data_len > read_len {
r.cfifoctr().modify(|r| r.set_bclr(true));
return Err(EndpointError::BufferOverflow);
}
for byte in buf[..read_len].iter_mut() {
*byte = r.cfifol().read().fifoport();
}
if data_len > read_len {
r.cfifoctr().modify(|r| r.set_bclr(true));
}
trace!("USB: ctrl data_out read {} bytes", read_len);
Ok(read_len)
}
async fn data_in(
&mut self,
data: &[u8],
_first: bool,
last: bool,
) -> Result<(), EndpointError> {
trace!("USB: ctrl data_in {} bytes, last={}", data.len(), last);
let r = I::regs();
if data.is_empty() && last {
r.dcpctr().modify(|r| {
r.set_pid(DcpctrPid::Buffer);
r.set_ccpl(true);
});
return Ok(());
}
STATE.pipe_bemp.fetch_and(!1, Ordering::AcqRel);
r.cfifosel().modify(|r| {
r.set_curpipe(CfifoselCurpipe::Default);
r.set_isel(true);
});
while !r.cfifoctr().read().frdy() {
cortex_m::asm::nop();
}
for &byte in data {
r.cfifol().write(|w| w.set_fifoport(byte));
}
r.cfifoctr().modify(|r| r.set_bval(true));
if last {
r.dcpctr().modify(|r| r.set_ccpl(true));
}
r.dcpctr().modify(|r| r.set_pid(DcpctrPid::Buffer));
core::future::poll_fn(|cx| {
STATE.ep_wakers[0].register(cx.waker());
if STATE.pipe_bemp.load(Ordering::Acquire) & 1 != 0 {
STATE.pipe_bemp.fetch_and(!1, Ordering::AcqRel);
return Poll::Ready(());
} else {
Poll::Pending
}
})
.await;
Ok(())
}
async fn accept(&mut self) {
debug!("USB: ctrl accept");
let r = I::regs();
r.dcpctr().modify(|r| {
r.set_pid(DcpctrPid::Buffer);
r.set_ccpl(true);
})
}
async fn reject(&mut self) {
debug!("USB: ctrl reject");
let r = I::regs();
r.dcpctr().modify(|r| r.set_pid(DcpctrPid::Stall2));
}
async fn accept_set_address(&mut self, addr: u8) {
debug!("USB: ctrl accept_set_address={}", addr);
self.accept().await;
}
}