summaryrefslogtreecommitdiff
path: root/embassy-nrf
diff options
context:
space:
mode:
authorDario Nieuwenhuis <dirbaio@dirbaio.net>2022-04-16 04:47:27 +0200
committerDario Nieuwenhuis <dirbaio@dirbaio.net>2022-04-23 01:11:10 +0200
commit092c2b7dfea146681cea01fd4e4105c21c62b61f (patch)
treebe32635e53177b36ef36939bcdd5eadcf3be3ab8 /embassy-nrf
parentea0a701ebd142ea42e05e51e844bd53cc9bdf354 (diff)
downloadembassy-092c2b7dfea146681cea01fd4e4105c21c62b61f.zip
usb: builtin handling of interface alternate settings
The stack reads its own descriptors to figure out which endpoints are used in which alt settings, and enables/disables them as needed. The ControlHandler has a callback so it can get notified of alternate setting changes, which is purely informative (it doesn't have to do anything).
Diffstat (limited to 'embassy-nrf')
-rw-r--r--embassy-nrf/src/usb.rs110
1 files changed, 67 insertions, 43 deletions
diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs
index b67201e6..c032b2cc 100644
--- a/embassy-nrf/src/usb.rs
+++ b/embassy-nrf/src/usb.rs
@@ -194,16 +194,12 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
fn into_bus(self) -> Self::Bus {
Bus {
phantom: PhantomData,
- alloc_in: self.alloc_in,
- alloc_out: self.alloc_out,
}
}
}
pub struct Bus<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
- alloc_in: Allocator,
- alloc_out: Allocator,
}
impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
@@ -264,7 +260,16 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
if regs.events_usbreset.read().bits() != 0 {
regs.events_usbreset.reset();
regs.intenset.write(|w| w.usbreset().set());
- self.set_configured(false);
+
+ // Disable all endpoints except EP0
+ regs.epinen.write(|w| unsafe { w.bits(0x01) });
+ regs.epouten.write(|w| unsafe { w.bits(0x01) });
+ READY_ENDPOINTS.store(In::mask(0), Ordering::Release);
+ for i in 1..=7 {
+ In::waker(i).wake();
+ Out::waker(i).wake();
+ }
+
return Poll::Ready(Event::Reset);
}
@@ -297,57 +302,76 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
}
#[inline]
- fn set_configured(&mut self, configured: bool) {
+ fn set_address(&mut self, _addr: u8) {
+ // Nothing to do, the peripheral handles this.
+ }
+
+ fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
+ Driver::<T>::set_stalled(ep_addr, stalled)
+ }
+
+ fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
+ Driver::<T>::is_stalled(ep_addr)
+ }
+
+ fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
let regs = T::regs();
- unsafe {
- if configured {
- // TODO: Initialize ISO buffers
+ let i = ep_addr.index();
+ let mask = 1 << i;
- regs.epinen.write(|w| w.bits(self.alloc_in.used.into()));
- regs.epouten.write(|w| w.bits(self.alloc_out.used.into()));
+ debug!("endpoint_set_enabled {:?} {}", ep_addr, enabled);
- for i in 1..8 {
- let out_enabled = self.alloc_out.used & (1 << i) != 0;
+ match ep_addr.direction() {
+ UsbDirection::In => {
+ let mut was_enabled = false;
+ regs.epinen.modify(|r, w| {
+ let mut bits = r.bits();
+ was_enabled = (bits & mask) != 0;
+ if enabled {
+ bits |= mask
+ } else {
+ bits &= !mask
+ }
+ unsafe { w.bits(bits) }
+ });
+ let ready_mask = In::mask(i);
+ if enabled {
+ if !was_enabled {
+ READY_ENDPOINTS.fetch_or(ready_mask, Ordering::AcqRel);
+ }
+ } else {
+ READY_ENDPOINTS.fetch_and(!ready_mask, Ordering::AcqRel);
+ }
+
+ In::waker(i).wake();
+ }
+ UsbDirection::Out => {
+ regs.epouten.modify(|r, w| {
+ let mut bits = r.bits();
+ if enabled {
+ bits |= mask
+ } else {
+ bits &= !mask
+ }
+ unsafe { w.bits(bits) }
+ });
+
+ let ready_mask = Out::mask(i);
+ if enabled {
// when first enabled, bulk/interrupt OUT endpoints will *not* receive data (the
// peripheral will NAK all incoming packets) until we write a zero to the SIZE
// register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the
// SIZE register
- if out_enabled {
- regs.size.epout[i].reset();
- }
+ regs.size.epout[i].reset();
+ } else {
+ READY_ENDPOINTS.fetch_and(!ready_mask, Ordering::AcqRel);
}
- // IN endpoints (low bits) default to ready.
- // OUT endpoints (high bits) default to NOT ready, they become ready when data comes in.
- READY_ENDPOINTS.store(0x0000FFFF, Ordering::Release);
- } else {
- // Disable all endpoints except EP0
- regs.epinen.write(|w| w.bits(0x01));
- regs.epouten.write(|w| w.bits(0x01));
-
- READY_ENDPOINTS.store(In::mask(0), Ordering::Release);
+ Out::waker(i).wake();
}
}
-
- for i in 1..=7 {
- In::waker(i).wake();
- Out::waker(i).wake();
- }
- }
-
- #[inline]
- fn set_device_address(&mut self, _addr: u8) {
- // Nothing to do, the peripheral handles this.
- }
-
- fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
- Driver::<T>::set_stalled(ep_addr, stalled)
- }
-
- fn is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
- Driver::<T>::is_stalled(ep_addr)
}
#[inline]