use core::{
marker::PhantomData,
ops::{BitAnd, Not},
sync::atomic::Ordering,
task::Poll,
};
use embassy_time::Timer;
use embassy_usb_driver::{Bus as DriverBus, EndpointAddress, Event, Unsupported};
use crate::{
pac::usbfs::vals::{DcpctrPid, PipectrPid},
usb::{EP_ADDR, Instance as UsbInstance, STATE},
};
pub(crate) enum BusEvent {
PowerDetected = 1 << 0,
PowerRemoved = 1 << 1,
Reset = 1 << 2,
Suspend = 1 << 3,
Resume = 1 << 4,
}
pub struct Bus<'a, I: UsbInstance> {
pub(crate) _phantom: PhantomData<&'a I>,
}
impl BitAnd<BusEvent> for u32 {
type Output = bool;
fn bitand(self, rhs: BusEvent) -> Self::Output {
self & rhs as u32 != 0
}
}
impl Not for BusEvent {
type Output = u32;
fn not(self) -> Self::Output {
!(self as u32)
}
}
impl<'a, I: UsbInstance> DriverBus for Bus<'a, I> {
async fn enable(&mut self) {
info!("USBFS: bus enable D+ pullup");
let r = I::regs();
r.syscfg().modify(|r| {
r.set_usbe(true);
r.set_dprpu(true);
});
let lnst = r.syssts0().read().lnst();
let syscfg = r.syscfg().read();
info!("USBFS: SYSCFG={:?}, LNST={:02b}", syscfg, lnst);
}
async fn disable(&mut self) {
info!("USBFS: disable");
let r = I::regs();
r.syscfg().modify(|r| r.set_dprpu(false));
r.syscfg().modify(|r| r.set_usbe(false));
}
async fn poll(&mut self) -> Event {
core::future::poll_fn(|cx| {
STATE.bus_waker.register(cx.waker());
let bits = STATE.pending_bus.load(Ordering::Acquire);
if bits == 0 {
return Poll::Pending;
}
if bits & BusEvent::PowerDetected {
STATE
.pending_bus
.fetch_and(!BusEvent::PowerDetected, Ordering::AcqRel);
debug!("USBFS: poll -> power detected");
return Poll::Ready(Event::PowerDetected);
}
if bits & BusEvent::PowerRemoved {
STATE
.pending_bus
.fetch_and(!BusEvent::PowerRemoved, Ordering::AcqRel);
debug!("USBFS: poll -> power removed");
return Poll::Ready(Event::PowerRemoved);
}
if bits & BusEvent::Reset {
STATE
.pending_bus
.fetch_and(!BusEvent::Reset, Ordering::AcqRel);
debug!("USBFS: poll -> reset");
return Poll::Ready(Event::Reset);
}
if bits & BusEvent::Suspend {
STATE
.pending_bus
.fetch_and(!BusEvent::Suspend, Ordering::AcqRel);
debug!("USBFS: poll -> suspend");
return Poll::Ready(Event::Suspend);
}
if bits & BusEvent::Resume {
STATE
.pending_bus
.fetch_and(!BusEvent::Resume, Ordering::AcqRel);
debug!("USBFS: poll -> resume");
return Poll::Ready(Event::Resume);
}
Poll::Pending
})
.await
}
fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
if ep_addr.index() == 0 {
return;
}
let r = I::regs();
if let Some(pipe) = pipe_for_ep(ep_addr) {
let pipenctr_idx = pipe - 1;
if enabled {
r.brdyenb().modify(|r| r.set_brdy(pipe, true));
r.bempenb().modify(|r| r.set_bemp(pipe, true));
r.pipectr(pipenctr_idx)
.modify(|r| r.set_pid(PipectrPid::Buffer));
STATE.pipe_enabled.fetch_or(1 << pipe, Ordering::Release);
if ep_addr.is_in() {
STATE.pipe_bemp.fetch_or(1 << pipe, Ordering::Release);
}
} else {
r.pipectr(pipenctr_idx)
.modify(|r| r.set_pid(PipectrPid::Nak));
STATE
.pipe_enabled
.fetch_and(!(1 << pipe), Ordering::Release);
r.brdyenb().modify(|r| r.set_brdy(pipe, false));
r.bempenb().modify(|r| r.set_bemp(pipe, false));
}
STATE.ep_wakers[pipe].wake();
}
}
fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
let r = I::regs();
if ep_addr.index() == 0 {
r.dcpctr().modify(|r| {
r.set_pid(if stalled {
DcpctrPid::Stall2
} else {
DcpctrPid::Nak
});
if !stalled {
r.set_sqclr(true);
}
});
return;
}
if let Some(pipe) = pipe_for_ep(ep_addr) {
let pipectr_idx = pipe - 1;
r.pipectr(pipectr_idx).modify(|r| {
r.set_pid(if stalled {
PipectrPid::Stall2
} else {
PipectrPid::Nak
});
if !stalled {
r.set_sqclr(true);
}
});
}
}
fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
let r = I::regs();
if ep_addr.index() == 0 {
let pid = r.dcpctr().read().pid();
return matches!(pid, DcpctrPid::Stall2 | DcpctrPid::Stall3);
}
if let Some(pipe) = pipe_for_ep(ep_addr) {
let pipectr_idx = pipe - 1;
let pid = r.pipectr(pipectr_idx).read().pid();
return matches!(pid, PipectrPid::Stall2 | PipectrPid::Stall3);
}
false
}
#[cfg(feature = "time-driver")]
async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
let r = I::regs();
r.dvstctr0().modify(|r| {
r.set_rwupe(true);
r.set_wkup(true);
});
Timer::after_millis(2).await;
r.dvstctr0().modify(|r| r.set_wkup(false));
Ok(())
}
#[cfg(not(feature = "time-driver"))]
async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
Err(Unsupported)
}
}
fn pipe_for_ep(ep_addr: EndpointAddress) -> Option<usize> {
let target: u8 = ep_addr.into();
for (i, ep) in EP_ADDR.iter().enumerate() {
if ep.load(Ordering::Acquire) == target {
return Some(i + 1);
}
}
None
}