Merge pull request #722 from embassy-rs/usb-altsettings

usb: builtin handling of interface alternate settings
This commit is contained in:
Dario Nieuwenhuis
2022-04-23 06:20:11 +02:00
committed by GitHub
11 changed files with 425 additions and 167 deletions

View File

@ -17,6 +17,8 @@ flavors = [
[features]
defmt = ["dep:defmt", "embassy/defmt", "embassy-usb?/defmt"]
# Enable nightly-only features
nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async"]

View File

@ -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]