From 5249996d282e1ae08cea1593193e2fe6ca20880a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:36:53 +0100 Subject: nrf/usb: switch to new interrupt binding, fix vbus detect on nrf53. --- embassy-nrf/src/usb.rs | 1028 --------------------- embassy-nrf/src/usb/mod.rs | 888 ++++++++++++++++++ embassy-nrf/src/usb/vbus_detect.rs | 177 ++++ examples/nrf52840/Cargo.toml | 7 +- examples/nrf52840/src/bin/usb_ethernet.rs | 12 +- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 16 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 16 +- examples/nrf52840/src/bin/usb_serial.rs | 16 +- examples/nrf52840/src/bin/usb_serial_multitask.rs | 51 +- examples/nrf52840/src/bin/usb_serial_winusb.rs | 14 +- 10 files changed, 1141 insertions(+), 1084 deletions(-) delete mode 100644 embassy-nrf/src/usb.rs create mode 100644 embassy-nrf/src/usb/mod.rs create mode 100644 embassy-nrf/src/usb/vbus_detect.rs diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs deleted file mode 100644 index cd142f00f..000000000 --- a/embassy-nrf/src/usb.rs +++ /dev/null @@ -1,1028 +0,0 @@ -//! Universal Serial Bus (USB) driver. - -#![macro_use] - -use core::future::poll_fn; -use core::marker::PhantomData; -use core::mem::MaybeUninit; -use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; -use core::task::Poll; - -use cortex_m::peripheral::NVIC; -use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; -use embassy_usb_driver as driver; -use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported}; -use pac::usbd::RegisterBlock; - -use crate::interrupt::{Interrupt, InterruptExt}; -use crate::util::slice_in_ram; -use crate::{pac, Peripheral}; - -const NEW_AW: AtomicWaker = AtomicWaker::new(); -static BUS_WAKER: AtomicWaker = NEW_AW; -static EP0_WAKER: AtomicWaker = NEW_AW; -static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; -static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; -static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); - -/// Trait for detecting USB VBUS power. -/// -/// There are multiple ways to detect USB power. The behavior -/// here provides a hook into determining whether it is. -pub trait VbusDetect { - /// Report whether power is detected. - /// - /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the - /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. - fn is_usb_detected(&self) -> bool; - - /// Wait until USB power is ready. - /// - /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the - /// `USBPWRRDY` event from the `POWER` peripheral. - async fn wait_power_ready(&mut self) -> Result<(), ()>; -} - -/// [`VbusDetect`] implementation using the native hardware POWER peripheral. -/// -/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces -/// to POWER. In that case, use [`VbusDetectSignal`]. -#[cfg(not(feature = "_nrf5340-app"))] -pub struct HardwareVbusDetect { - _private: (), -} - -static POWER_WAKER: AtomicWaker = NEW_AW; - -#[cfg(not(feature = "_nrf5340-app"))] -impl HardwareVbusDetect { - /// Create a new `VbusDetectNative`. - pub fn new(power_irq: impl Interrupt) -> Self { - let regs = unsafe { &*pac::POWER::ptr() }; - - power_irq.set_handler(Self::on_interrupt); - power_irq.unpend(); - power_irq.enable(); - - regs.intenset - .write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set()); - - Self { _private: () } - } - - #[cfg(not(feature = "_nrf5340-app"))] - fn on_interrupt(_: *mut ()) { - let regs = unsafe { &*pac::POWER::ptr() }; - - if regs.events_usbdetected.read().bits() != 0 { - regs.events_usbdetected.reset(); - BUS_WAKER.wake(); - } - - if regs.events_usbremoved.read().bits() != 0 { - regs.events_usbremoved.reset(); - BUS_WAKER.wake(); - POWER_WAKER.wake(); - } - - if regs.events_usbpwrrdy.read().bits() != 0 { - regs.events_usbpwrrdy.reset(); - POWER_WAKER.wake(); - } - } -} - -#[cfg(not(feature = "_nrf5340-app"))] -impl VbusDetect for HardwareVbusDetect { - fn is_usb_detected(&self) -> bool { - let regs = unsafe { &*pac::POWER::ptr() }; - regs.usbregstatus.read().vbusdetect().is_vbus_present() - } - - async fn wait_power_ready(&mut self) -> Result<(), ()> { - poll_fn(move |cx| { - POWER_WAKER.register(cx.waker()); - let regs = unsafe { &*pac::POWER::ptr() }; - - if regs.usbregstatus.read().outputrdy().is_ready() { - Poll::Ready(Ok(())) - } else if !self.is_usb_detected() { - Poll::Ready(Err(())) - } else { - Poll::Pending - } - }) - .await - } -} - -/// Software-backed [`VbusDetect`] implementation. -/// -/// This implementation does not interact with the hardware, it allows user code -/// to notify the power events by calling functions instead. -/// -/// This is suitable for use with the nRF softdevice, by calling the functions -/// when the softdevice reports power-related events. -pub struct SoftwareVbusDetect { - usb_detected: AtomicBool, - power_ready: AtomicBool, -} - -impl SoftwareVbusDetect { - /// Create a new `SoftwareVbusDetect`. - pub fn new(usb_detected: bool, power_ready: bool) -> Self { - BUS_WAKER.wake(); - - Self { - usb_detected: AtomicBool::new(usb_detected), - power_ready: AtomicBool::new(power_ready), - } - } - - /// Report whether power was detected. - /// - /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. - pub fn detected(&self, detected: bool) { - self.usb_detected.store(detected, Ordering::Relaxed); - self.power_ready.store(false, Ordering::Relaxed); - BUS_WAKER.wake(); - POWER_WAKER.wake(); - } - - /// Report when USB power is ready. - /// - /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. - pub fn ready(&self) { - self.power_ready.store(true, Ordering::Relaxed); - POWER_WAKER.wake(); - } -} - -impl VbusDetect for &SoftwareVbusDetect { - fn is_usb_detected(&self) -> bool { - self.usb_detected.load(Ordering::Relaxed) - } - - async fn wait_power_ready(&mut self) -> Result<(), ()> { - poll_fn(move |cx| { - POWER_WAKER.register(cx.waker()); - - if self.power_ready.load(Ordering::Relaxed) { - Poll::Ready(Ok(())) - } else if !self.usb_detected.load(Ordering::Relaxed) { - Poll::Ready(Err(())) - } else { - Poll::Pending - } - }) - .await - } -} - -/// USB driver. -pub struct Driver<'d, T: Instance, P: VbusDetect> { - _p: PeripheralRef<'d, T>, - alloc_in: Allocator, - alloc_out: Allocator, - usb_supply: P, -} - -impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> { - /// Create a new USB driver. - pub fn new(usb: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, usb_supply: P) -> Self { - into_ref!(usb, irq); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self { - _p: usb, - alloc_in: Allocator::new(), - alloc_out: Allocator::new(), - usb_supply, - } - } - - fn on_interrupt(_: *mut ()) { - let regs = T::regs(); - - if regs.events_usbreset.read().bits() != 0 { - regs.intenclr.write(|w| w.usbreset().clear()); - BUS_WAKER.wake(); - EP0_WAKER.wake(); - } - - if regs.events_ep0setup.read().bits() != 0 { - regs.intenclr.write(|w| w.ep0setup().clear()); - EP0_WAKER.wake(); - } - - if regs.events_ep0datadone.read().bits() != 0 { - regs.intenclr.write(|w| w.ep0datadone().clear()); - EP0_WAKER.wake(); - } - - // USBEVENT and EPDATA events are weird. They're the "aggregate" - // of individual bits in EVENTCAUSE and EPDATASTATUS. We handle them - // differently than events normally. - // - // They seem to be edge-triggered, not level-triggered: when an - // individual bit goes 0->1, the event fires *just once*. - // Therefore, it's fine to clear just the event, and let main thread - // check the individual bits in EVENTCAUSE and EPDATASTATUS. It - // doesn't cause an infinite irq loop. - if regs.events_usbevent.read().bits() != 0 { - regs.events_usbevent.reset(); - BUS_WAKER.wake(); - } - - if regs.events_epdata.read().bits() != 0 { - regs.events_epdata.reset(); - - let r = regs.epdatastatus.read().bits(); - regs.epdatastatus.write(|w| unsafe { w.bits(r) }); - READY_ENDPOINTS.fetch_or(r, Ordering::AcqRel); - for i in 1..=7 { - if r & In::mask(i) != 0 { - In::waker(i).wake(); - } - if r & Out::mask(i) != 0 { - Out::waker(i).wake(); - } - } - } - } -} - -impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P> { - type EndpointOut = Endpoint<'d, T, Out>; - type EndpointIn = Endpoint<'d, T, In>; - type ControlPipe = ControlPipe<'d, T>; - type Bus = Bus<'d, T, P>; - - fn alloc_endpoint_in( - &mut self, - ep_type: EndpointType, - packet_size: u16, - interval_ms: u8, - ) -> Result { - let index = self.alloc_in.allocate(ep_type)?; - let ep_addr = EndpointAddress::from_parts(index, Direction::In); - Ok(Endpoint::new(EndpointInfo { - addr: ep_addr, - ep_type, - max_packet_size: packet_size, - interval_ms, - })) - } - - fn alloc_endpoint_out( - &mut self, - ep_type: EndpointType, - packet_size: u16, - interval_ms: u8, - ) -> Result { - let index = self.alloc_out.allocate(ep_type)?; - let ep_addr = EndpointAddress::from_parts(index, Direction::Out); - Ok(Endpoint::new(EndpointInfo { - addr: ep_addr, - ep_type, - max_packet_size: packet_size, - interval_ms, - })) - } - - fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { - ( - Bus { - _p: unsafe { self._p.clone_unchecked() }, - power_available: false, - usb_supply: self.usb_supply, - }, - ControlPipe { - _p: self._p, - max_packet_size: control_max_packet_size, - }, - ) - } -} - -/// USB bus. -pub struct Bus<'d, T: Instance, P: VbusDetect> { - _p: PeripheralRef<'d, T>, - power_available: bool, - usb_supply: P, -} - -impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { - async fn enable(&mut self) { - let regs = T::regs(); - - errata::pre_enable(); - - regs.enable.write(|w| w.enable().enabled()); - - // Wait until the peripheral is ready. - regs.intenset.write(|w| w.usbevent().set_bit()); - poll_fn(|cx| { - BUS_WAKER.register(cx.waker()); - if regs.eventcause.read().ready().is_ready() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - regs.eventcause.write(|w| w.ready().clear_bit_by_one()); - - errata::post_enable(); - - unsafe { NVIC::unmask(pac::Interrupt::USBD) }; - - regs.intenset.write(|w| { - w.usbreset().set_bit(); - w.usbevent().set_bit(); - w.epdata().set_bit(); - w - }); - - if self.usb_supply.wait_power_ready().await.is_ok() { - // Enable the USB pullup, allowing enumeration. - regs.usbpullup.write(|w| w.connect().enabled()); - trace!("enabled"); - } else { - trace!("usb power not ready due to usb removal"); - } - } - - async fn disable(&mut self) { - let regs = T::regs(); - regs.enable.write(|x| x.enable().disabled()); - } - - async fn poll(&mut self) -> Event { - poll_fn(move |cx| { - BUS_WAKER.register(cx.waker()); - let regs = T::regs(); - - if regs.events_usbreset.read().bits() != 0 { - regs.events_usbreset.reset(); - regs.intenset.write(|w| w.usbreset().set()); - - // 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); - } - - let r = regs.eventcause.read(); - - if r.isooutcrc().bit() { - regs.eventcause.write(|w| w.isooutcrc().detected()); - trace!("USB event: isooutcrc"); - } - if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().allowed()); - trace!("USB event: usbwuallowed"); - } - if r.suspend().bit() { - regs.eventcause.write(|w| w.suspend().detected()); - regs.lowpower.write(|w| w.lowpower().low_power()); - return Poll::Ready(Event::Suspend); - } - if r.resume().bit() { - regs.eventcause.write(|w| w.resume().detected()); - return Poll::Ready(Event::Resume); - } - if r.ready().bit() { - regs.eventcause.write(|w| w.ready().ready()); - trace!("USB event: ready"); - } - - if self.usb_supply.is_usb_detected() != self.power_available { - self.power_available = !self.power_available; - if self.power_available { - trace!("Power event: available"); - return Poll::Ready(Event::PowerDetected); - } else { - trace!("Power event: removed"); - return Poll::Ready(Event::PowerRemoved); - } - } - - Poll::Pending - }) - .await - } - - fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { - let regs = T::regs(); - unsafe { - if ep_addr.index() == 0 { - regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(stalled)); - } else { - regs.epstall.write(|w| { - w.ep().bits(ep_addr.index() as u8 & 0b111); - w.io().bit(ep_addr.is_in()); - w.stall().bit(stalled) - }); - } - } - } - - fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { - let regs = T::regs(); - let i = ep_addr.index(); - match ep_addr.direction() { - Direction::Out => regs.halted.epout[i].read().getstatus().is_halted(), - Direction::In => regs.halted.epin[i].read().getstatus().is_halted(), - } - } - - fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { - let regs = T::regs(); - - let i = ep_addr.index(); - let mask = 1 << i; - - debug!("endpoint_set_enabled {:?} {}", ep_addr, enabled); - - match ep_addr.direction() { - Direction::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(); - } - Direction::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 - regs.size.epout[i].reset(); - } else { - READY_ENDPOINTS.fetch_and(!ready_mask, Ordering::AcqRel); - } - - Out::waker(i).wake(); - } - } - } - - #[inline] - async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { - let regs = T::regs(); - - if regs.lowpower.read().lowpower().is_low_power() { - errata::pre_wakeup(); - - regs.lowpower.write(|w| w.lowpower().force_normal()); - - poll_fn(|cx| { - BUS_WAKER.register(cx.waker()); - let regs = T::regs(); - let r = regs.eventcause.read(); - - if regs.events_usbreset.read().bits() != 0 { - Poll::Ready(()) - } else if r.resume().bit() { - Poll::Ready(()) - } else if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().allowed()); - - regs.dpdmvalue.write(|w| w.state().resume()); - regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit()); - - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - errata::post_wakeup(); - } - - Ok(()) - } -} - -/// Type-level marker for OUT endpoints. -pub enum Out {} - -/// Type-level marker for IN endpoints. -pub enum In {} - -trait EndpointDir { - fn waker(i: usize) -> &'static AtomicWaker; - fn mask(i: usize) -> u32; - fn is_enabled(regs: &RegisterBlock, i: usize) -> bool; -} - -impl EndpointDir for In { - #[inline] - fn waker(i: usize) -> &'static AtomicWaker { - &EP_IN_WAKERS[i - 1] - } - - #[inline] - fn mask(i: usize) -> u32 { - 1 << i - } - - #[inline] - fn is_enabled(regs: &RegisterBlock, i: usize) -> bool { - (regs.epinen.read().bits() & (1 << i)) != 0 - } -} - -impl EndpointDir for Out { - #[inline] - fn waker(i: usize) -> &'static AtomicWaker { - &EP_OUT_WAKERS[i - 1] - } - - #[inline] - fn mask(i: usize) -> u32 { - 1 << (i + 16) - } - - #[inline] - fn is_enabled(regs: &RegisterBlock, i: usize) -> bool { - (regs.epouten.read().bits() & (1 << i)) != 0 - } -} - -/// USB endpoint. -pub struct Endpoint<'d, T: Instance, Dir> { - _phantom: PhantomData<(&'d mut T, Dir)>, - info: EndpointInfo, -} - -impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { - fn new(info: EndpointInfo) -> Self { - Self { - info, - _phantom: PhantomData, - } - } -} - -impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir> { - fn info(&self) -> &EndpointInfo { - &self.info - } - - async fn wait_enabled(&mut self) { - let i = self.info.addr.index(); - assert!(i != 0); - - poll_fn(move |cx| { - Dir::waker(i).register(cx.waker()); - if Dir::is_enabled(T::regs(), i) { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await - } -} - -impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { - async fn wait_data_ready(&mut self) -> Result<(), ()> - where - Dir: EndpointDir, - { - let i = self.info.addr.index(); - assert!(i != 0); - poll_fn(|cx| { - Dir::waker(i).register(cx.waker()); - let r = READY_ENDPOINTS.load(Ordering::Acquire); - if !Dir::is_enabled(T::regs(), i) { - Poll::Ready(Err(())) - } else if r & Dir::mask(i) != 0 { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - }) - .await?; - - // Mark as not ready - READY_ENDPOINTS.fetch_and(!Dir::mask(i), Ordering::AcqRel); - - Ok(()) - } -} - -unsafe fn read_dma(i: usize, buf: &mut [u8]) -> Result { - let regs = T::regs(); - - // Check that the packet fits into the buffer - let size = regs.size.epout[i].read().bits() as usize; - if size > buf.len() { - return Err(EndpointError::BufferOverflow); - } - - let epout = [ - ®s.epout0, - ®s.epout1, - ®s.epout2, - ®s.epout3, - ®s.epout4, - ®s.epout5, - ®s.epout6, - ®s.epout7, - ]; - epout[i].ptr.write(|w| w.bits(buf.as_ptr() as u32)); - // MAXCNT must match SIZE - epout[i].maxcnt.write(|w| w.bits(size as u32)); - - dma_start(); - regs.events_endepout[i].reset(); - regs.tasks_startepout[i].write(|w| w.tasks_startepout().set_bit()); - while regs.events_endepout[i].read().events_endepout().bit_is_clear() {} - regs.events_endepout[i].reset(); - dma_end(); - - regs.size.epout[i].reset(); - - Ok(size) -} - -unsafe fn write_dma(i: usize, buf: &[u8]) { - let regs = T::regs(); - assert!(buf.len() <= 64); - - let mut ram_buf: MaybeUninit<[u8; 64]> = MaybeUninit::uninit(); - let ptr = if !slice_in_ram(buf) { - // EasyDMA can't read FLASH, so we copy through RAM - let ptr = ram_buf.as_mut_ptr() as *mut u8; - core::ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len()); - ptr - } else { - buf.as_ptr() - }; - - let epin = [ - ®s.epin0, - ®s.epin1, - ®s.epin2, - ®s.epin3, - ®s.epin4, - ®s.epin5, - ®s.epin6, - ®s.epin7, - ]; - - // Set the buffer length so the right number of bytes are transmitted. - // Safety: `buf.len()` has been checked to be <= the max buffer length. - epin[i].ptr.write(|w| w.bits(ptr as u32)); - epin[i].maxcnt.write(|w| w.maxcnt().bits(buf.len() as u8)); - - regs.events_endepin[i].reset(); - - dma_start(); - regs.tasks_startepin[i].write(|w| w.bits(1)); - while regs.events_endepin[i].read().bits() == 0 {} - dma_end(); -} - -impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { - async fn read(&mut self, buf: &mut [u8]) -> Result { - let i = self.info.addr.index(); - assert!(i != 0); - - self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; - - unsafe { read_dma::(i, buf) } - } -} - -impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { - async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { - let i = self.info.addr.index(); - assert!(i != 0); - - self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; - - unsafe { write_dma::(i, buf) } - - Ok(()) - } -} - -/// USB control pipe. -pub struct ControlPipe<'d, T: Instance> { - _p: PeripheralRef<'d, T>, - max_packet_size: u16, -} - -impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { - fn max_packet_size(&self) -> usize { - usize::from(self.max_packet_size) - } - - async fn setup(&mut self) -> [u8; 8] { - let regs = T::regs(); - - // Reset shorts - regs.shorts.write(|w| w); - - // Wait for SETUP packet - regs.intenset.write(|w| w.ep0setup().set()); - poll_fn(|cx| { - EP0_WAKER.register(cx.waker()); - let regs = T::regs(); - if regs.events_ep0setup.read().bits() != 0 { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - regs.events_ep0setup.reset(); - - let mut buf = [0; 8]; - buf[0] = regs.bmrequesttype.read().bits() as u8; - buf[1] = regs.brequest.read().brequest().bits(); - buf[2] = regs.wvaluel.read().wvaluel().bits(); - buf[3] = regs.wvalueh.read().wvalueh().bits(); - buf[4] = regs.windexl.read().windexl().bits(); - buf[5] = regs.windexh.read().windexh().bits(); - buf[6] = regs.wlengthl.read().wlengthl().bits(); - buf[7] = regs.wlengthh.read().wlengthh().bits(); - - buf - } - - async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result { - let regs = T::regs(); - - regs.events_ep0datadone.reset(); - - // This starts a RX on EP0. events_ep0datadone notifies when done. - regs.tasks_ep0rcvout.write(|w| w.tasks_ep0rcvout().set_bit()); - - // Wait until ready - regs.intenset.write(|w| { - w.usbreset().set(); - w.ep0setup().set(); - w.ep0datadone().set() - }); - poll_fn(|cx| { - EP0_WAKER.register(cx.waker()); - let regs = T::regs(); - if regs.events_ep0datadone.read().bits() != 0 { - Poll::Ready(Ok(())) - } else if regs.events_usbreset.read().bits() != 0 { - trace!("aborted control data_out: usb reset"); - Poll::Ready(Err(EndpointError::Disabled)) - } else if regs.events_ep0setup.read().bits() != 0 { - trace!("aborted control data_out: received another SETUP"); - Poll::Ready(Err(EndpointError::Disabled)) - } else { - Poll::Pending - } - }) - .await?; - - unsafe { read_dma::(0, buf) } - } - - async fn data_in(&mut self, buf: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> { - let regs = T::regs(); - regs.events_ep0datadone.reset(); - - regs.shorts.write(|w| w.ep0datadone_ep0status().bit(last)); - - // This starts a TX on EP0. events_ep0datadone notifies when done. - unsafe { write_dma::(0, buf) } - - regs.intenset.write(|w| { - w.usbreset().set(); - w.ep0setup().set(); - w.ep0datadone().set() - }); - - poll_fn(|cx| { - cx.waker().wake_by_ref(); - EP0_WAKER.register(cx.waker()); - let regs = T::regs(); - if regs.events_ep0datadone.read().bits() != 0 { - Poll::Ready(Ok(())) - } else if regs.events_usbreset.read().bits() != 0 { - trace!("aborted control data_in: usb reset"); - Poll::Ready(Err(EndpointError::Disabled)) - } else if regs.events_ep0setup.read().bits() != 0 { - trace!("aborted control data_in: received another SETUP"); - Poll::Ready(Err(EndpointError::Disabled)) - } else { - Poll::Pending - } - }) - .await - } - - async fn accept(&mut self) { - let regs = T::regs(); - regs.tasks_ep0status.write(|w| w.tasks_ep0status().bit(true)); - } - - async fn reject(&mut self) { - let regs = T::regs(); - regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); - } - - async fn accept_set_address(&mut self, _addr: u8) { - self.accept().await; - // Nothing to do, the peripheral handles this. - } -} - -fn dma_start() { - compiler_fence(Ordering::Release); -} - -fn dma_end() { - compiler_fence(Ordering::Acquire); -} - -struct Allocator { - used: u16, -} - -impl Allocator { - fn new() -> Self { - Self { used: 0 } - } - - fn allocate(&mut self, ep_type: EndpointType) -> Result { - // Endpoint addresses are fixed in hardware: - // - 0x80 / 0x00 - Control EP0 - // - 0x81 / 0x01 - Bulk/Interrupt EP1 - // - 0x82 / 0x02 - Bulk/Interrupt EP2 - // - 0x83 / 0x03 - Bulk/Interrupt EP3 - // - 0x84 / 0x04 - Bulk/Interrupt EP4 - // - 0x85 / 0x05 - Bulk/Interrupt EP5 - // - 0x86 / 0x06 - Bulk/Interrupt EP6 - // - 0x87 / 0x07 - Bulk/Interrupt EP7 - // - 0x88 / 0x08 - Isochronous - - // Endpoint directions are allocated individually. - - let alloc_index = match ep_type { - EndpointType::Isochronous => 8, - EndpointType::Control => return Err(driver::EndpointAllocError), - EndpointType::Interrupt | EndpointType::Bulk => { - // Find rightmost zero bit in 1..=7 - let ones = (self.used >> 1).trailing_ones() as usize; - if ones >= 7 { - return Err(driver::EndpointAllocError); - } - ones + 1 - } - }; - - if self.used & (1 << alloc_index) != 0 { - return Err(driver::EndpointAllocError); - } - - self.used |= 1 << alloc_index; - - Ok(alloc_index) - } -} - -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - fn regs() -> &'static pac::usbd::RegisterBlock; - } -} - -/// USB peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { - /// Interrupt for this peripheral. - type Interrupt: Interrupt; -} - -macro_rules! impl_usb { - ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::usb::sealed::Instance for peripherals::$type { - fn regs() -> &'static pac::usbd::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } - } - } - impl crate::usb::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; - } - }; -} - -mod errata { - - /// Writes `val` to `addr`. Used to apply Errata workarounds. - #[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "nrf52820"))] - unsafe fn poke(addr: u32, val: u32) { - (addr as *mut u32).write_volatile(val); - } - - /// Reads 32 bits from `addr`. - #[cfg(feature = "nrf52840")] - unsafe fn peek(addr: u32) -> u32 { - (addr as *mut u32).read_volatile() - } - - pub fn pre_enable() { - // Works around Erratum 187 on chip revisions 1 and 2. - #[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "nrf52820"))] - unsafe { - poke(0x4006EC00, 0x00009375); - poke(0x4006ED14, 0x00000003); - poke(0x4006EC00, 0x00009375); - } - - pre_wakeup(); - } - - pub fn post_enable() { - post_wakeup(); - - // Works around Erratum 187 on chip revisions 1 and 2. - #[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "nrf52820"))] - unsafe { - poke(0x4006EC00, 0x00009375); - poke(0x4006ED14, 0x00000000); - poke(0x4006EC00, 0x00009375); - } - } - - pub fn pre_wakeup() { - // Works around Erratum 171 on chip revisions 1 and 2. - - #[cfg(feature = "nrf52840")] - unsafe { - if peek(0x4006EC00) == 0x00000000 { - poke(0x4006EC00, 0x00009375); - } - - poke(0x4006EC14, 0x000000C0); - poke(0x4006EC00, 0x00009375); - } - } - - pub fn post_wakeup() { - // Works around Erratum 171 on chip revisions 1 and 2. - - #[cfg(feature = "nrf52840")] - unsafe { - if peek(0x4006EC00) == 0x00000000 { - poke(0x4006EC00, 0x00009375); - } - - poke(0x4006EC14, 0x00000000); - poke(0x4006EC00, 0x00009375); - } - } -} diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs new file mode 100644 index 000000000..56de511df --- /dev/null +++ b/embassy-nrf/src/usb/mod.rs @@ -0,0 +1,888 @@ +//! Universal Serial Bus (USB) driver. + +#![macro_use] + +pub mod vbus_detect; + +use core::future::poll_fn; +use core::marker::PhantomData; +use core::mem::MaybeUninit; +use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; +use core::task::Poll; + +use cortex_m::peripheral::NVIC; +use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +use embassy_usb_driver as driver; +use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported}; +use pac::usbd::RegisterBlock; + +use self::vbus_detect::VbusDetect; +use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::util::slice_in_ram; +use crate::{pac, Peripheral}; + +const NEW_AW: AtomicWaker = AtomicWaker::new(); +static BUS_WAKER: AtomicWaker = NEW_AW; +static EP0_WAKER: AtomicWaker = NEW_AW; +static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; +static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; +static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = T::regs(); + + if regs.events_usbreset.read().bits() != 0 { + regs.intenclr.write(|w| w.usbreset().clear()); + BUS_WAKER.wake(); + EP0_WAKER.wake(); + } + + if regs.events_ep0setup.read().bits() != 0 { + regs.intenclr.write(|w| w.ep0setup().clear()); + EP0_WAKER.wake(); + } + + if regs.events_ep0datadone.read().bits() != 0 { + regs.intenclr.write(|w| w.ep0datadone().clear()); + EP0_WAKER.wake(); + } + + // USBEVENT and EPDATA events are weird. They're the "aggregate" + // of individual bits in EVENTCAUSE and EPDATASTATUS. We handle them + // differently than events normally. + // + // They seem to be edge-triggered, not level-triggered: when an + // individual bit goes 0->1, the event fires *just once*. + // Therefore, it's fine to clear just the event, and let main thread + // check the individual bits in EVENTCAUSE and EPDATASTATUS. It + // doesn't cause an infinite irq loop. + if regs.events_usbevent.read().bits() != 0 { + regs.events_usbevent.reset(); + BUS_WAKER.wake(); + } + + if regs.events_epdata.read().bits() != 0 { + regs.events_epdata.reset(); + + let r = regs.epdatastatus.read().bits(); + regs.epdatastatus.write(|w| unsafe { w.bits(r) }); + READY_ENDPOINTS.fetch_or(r, Ordering::AcqRel); + for i in 1..=7 { + if r & In::mask(i) != 0 { + In::waker(i).wake(); + } + if r & Out::mask(i) != 0 { + Out::waker(i).wake(); + } + } + } + } +} + +/// USB driver. +pub struct Driver<'d, T: Instance, V: VbusDetect> { + _p: PeripheralRef<'d, T>, + alloc_in: Allocator, + alloc_out: Allocator, + vbus_detect: V, +} + +impl<'d, T: Instance, V: VbusDetect> Driver<'d, T, V> { + /// Create a new USB driver. + pub fn new( + usb: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + vbus_detect: V, + ) -> Self { + into_ref!(usb); + + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + + Self { + _p: usb, + alloc_in: Allocator::new(), + alloc_out: Allocator::new(), + vbus_detect, + } + } +} + +impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V> { + type EndpointOut = Endpoint<'d, T, Out>; + type EndpointIn = Endpoint<'d, T, In>; + type ControlPipe = ControlPipe<'d, T>; + type Bus = Bus<'d, T, V>; + + fn alloc_endpoint_in( + &mut self, + ep_type: EndpointType, + packet_size: u16, + interval_ms: u8, + ) -> Result { + let index = self.alloc_in.allocate(ep_type)?; + let ep_addr = EndpointAddress::from_parts(index, Direction::In); + Ok(Endpoint::new(EndpointInfo { + addr: ep_addr, + ep_type, + max_packet_size: packet_size, + interval_ms, + })) + } + + fn alloc_endpoint_out( + &mut self, + ep_type: EndpointType, + packet_size: u16, + interval_ms: u8, + ) -> Result { + let index = self.alloc_out.allocate(ep_type)?; + let ep_addr = EndpointAddress::from_parts(index, Direction::Out); + Ok(Endpoint::new(EndpointInfo { + addr: ep_addr, + ep_type, + max_packet_size: packet_size, + interval_ms, + })) + } + + fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { + ( + Bus { + _p: unsafe { self._p.clone_unchecked() }, + power_available: false, + vbus_detect: self.vbus_detect, + }, + ControlPipe { + _p: self._p, + max_packet_size: control_max_packet_size, + }, + ) + } +} + +/// USB bus. +pub struct Bus<'d, T: Instance, V: VbusDetect> { + _p: PeripheralRef<'d, T>, + power_available: bool, + vbus_detect: V, +} + +impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { + async fn enable(&mut self) { + let regs = T::regs(); + + errata::pre_enable(); + + regs.enable.write(|w| w.enable().enabled()); + + // Wait until the peripheral is ready. + regs.intenset.write(|w| w.usbevent().set_bit()); + poll_fn(|cx| { + BUS_WAKER.register(cx.waker()); + if regs.eventcause.read().ready().is_ready() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + regs.eventcause.write(|w| w.ready().clear_bit_by_one()); + + errata::post_enable(); + + unsafe { NVIC::unmask(pac::Interrupt::USBD) }; + + regs.intenset.write(|w| { + w.usbreset().set_bit(); + w.usbevent().set_bit(); + w.epdata().set_bit(); + w + }); + + if self.vbus_detect.wait_power_ready().await.is_ok() { + // Enable the USB pullup, allowing enumeration. + regs.usbpullup.write(|w| w.connect().enabled()); + trace!("enabled"); + } else { + trace!("usb power not ready due to usb removal"); + } + } + + async fn disable(&mut self) { + let regs = T::regs(); + regs.enable.write(|x| x.enable().disabled()); + } + + async fn poll(&mut self) -> Event { + poll_fn(move |cx| { + BUS_WAKER.register(cx.waker()); + let regs = T::regs(); + + if regs.events_usbreset.read().bits() != 0 { + regs.events_usbreset.reset(); + regs.intenset.write(|w| w.usbreset().set()); + + // 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); + } + + let r = regs.eventcause.read(); + + if r.isooutcrc().bit() { + regs.eventcause.write(|w| w.isooutcrc().detected()); + trace!("USB event: isooutcrc"); + } + if r.usbwuallowed().bit() { + regs.eventcause.write(|w| w.usbwuallowed().allowed()); + trace!("USB event: usbwuallowed"); + } + if r.suspend().bit() { + regs.eventcause.write(|w| w.suspend().detected()); + regs.lowpower.write(|w| w.lowpower().low_power()); + return Poll::Ready(Event::Suspend); + } + if r.resume().bit() { + regs.eventcause.write(|w| w.resume().detected()); + return Poll::Ready(Event::Resume); + } + if r.ready().bit() { + regs.eventcause.write(|w| w.ready().ready()); + trace!("USB event: ready"); + } + + if self.vbus_detect.is_usb_detected() != self.power_available { + self.power_available = !self.power_available; + if self.power_available { + trace!("Power event: available"); + return Poll::Ready(Event::PowerDetected); + } else { + trace!("Power event: removed"); + return Poll::Ready(Event::PowerRemoved); + } + } + + Poll::Pending + }) + .await + } + + fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { + let regs = T::regs(); + unsafe { + if ep_addr.index() == 0 { + regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(stalled)); + } else { + regs.epstall.write(|w| { + w.ep().bits(ep_addr.index() as u8 & 0b111); + w.io().bit(ep_addr.is_in()); + w.stall().bit(stalled) + }); + } + } + } + + fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { + let regs = T::regs(); + let i = ep_addr.index(); + match ep_addr.direction() { + Direction::Out => regs.halted.epout[i].read().getstatus().is_halted(), + Direction::In => regs.halted.epin[i].read().getstatus().is_halted(), + } + } + + fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { + let regs = T::regs(); + + let i = ep_addr.index(); + let mask = 1 << i; + + debug!("endpoint_set_enabled {:?} {}", ep_addr, enabled); + + match ep_addr.direction() { + Direction::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(); + } + Direction::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 + regs.size.epout[i].reset(); + } else { + READY_ENDPOINTS.fetch_and(!ready_mask, Ordering::AcqRel); + } + + Out::waker(i).wake(); + } + } + } + + #[inline] + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + let regs = T::regs(); + + if regs.lowpower.read().lowpower().is_low_power() { + errata::pre_wakeup(); + + regs.lowpower.write(|w| w.lowpower().force_normal()); + + poll_fn(|cx| { + BUS_WAKER.register(cx.waker()); + let regs = T::regs(); + let r = regs.eventcause.read(); + + if regs.events_usbreset.read().bits() != 0 { + Poll::Ready(()) + } else if r.resume().bit() { + Poll::Ready(()) + } else if r.usbwuallowed().bit() { + regs.eventcause.write(|w| w.usbwuallowed().allowed()); + + regs.dpdmvalue.write(|w| w.state().resume()); + regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit()); + + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + errata::post_wakeup(); + } + + Ok(()) + } +} + +/// Type-level marker for OUT endpoints. +pub enum Out {} + +/// Type-level marker for IN endpoints. +pub enum In {} + +trait EndpointDir { + fn waker(i: usize) -> &'static AtomicWaker; + fn mask(i: usize) -> u32; + fn is_enabled(regs: &RegisterBlock, i: usize) -> bool; +} + +impl EndpointDir for In { + #[inline] + fn waker(i: usize) -> &'static AtomicWaker { + &EP_IN_WAKERS[i - 1] + } + + #[inline] + fn mask(i: usize) -> u32 { + 1 << i + } + + #[inline] + fn is_enabled(regs: &RegisterBlock, i: usize) -> bool { + (regs.epinen.read().bits() & (1 << i)) != 0 + } +} + +impl EndpointDir for Out { + #[inline] + fn waker(i: usize) -> &'static AtomicWaker { + &EP_OUT_WAKERS[i - 1] + } + + #[inline] + fn mask(i: usize) -> u32 { + 1 << (i + 16) + } + + #[inline] + fn is_enabled(regs: &RegisterBlock, i: usize) -> bool { + (regs.epouten.read().bits() & (1 << i)) != 0 + } +} + +/// USB endpoint. +pub struct Endpoint<'d, T: Instance, Dir> { + _phantom: PhantomData<(&'d mut T, Dir)>, + info: EndpointInfo, +} + +impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { + fn new(info: EndpointInfo) -> Self { + Self { + info, + _phantom: PhantomData, + } + } +} + +impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir> { + fn info(&self) -> &EndpointInfo { + &self.info + } + + async fn wait_enabled(&mut self) { + let i = self.info.addr.index(); + assert!(i != 0); + + poll_fn(move |cx| { + Dir::waker(i).register(cx.waker()); + if Dir::is_enabled(T::regs(), i) { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } +} + +impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { + async fn wait_data_ready(&mut self) -> Result<(), ()> + where + Dir: EndpointDir, + { + let i = self.info.addr.index(); + assert!(i != 0); + poll_fn(|cx| { + Dir::waker(i).register(cx.waker()); + let r = READY_ENDPOINTS.load(Ordering::Acquire); + if !Dir::is_enabled(T::regs(), i) { + Poll::Ready(Err(())) + } else if r & Dir::mask(i) != 0 { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }) + .await?; + + // Mark as not ready + READY_ENDPOINTS.fetch_and(!Dir::mask(i), Ordering::AcqRel); + + Ok(()) + } +} + +unsafe fn read_dma(i: usize, buf: &mut [u8]) -> Result { + let regs = T::regs(); + + // Check that the packet fits into the buffer + let size = regs.size.epout[i].read().bits() as usize; + if size > buf.len() { + return Err(EndpointError::BufferOverflow); + } + + let epout = [ + ®s.epout0, + ®s.epout1, + ®s.epout2, + ®s.epout3, + ®s.epout4, + ®s.epout5, + ®s.epout6, + ®s.epout7, + ]; + epout[i].ptr.write(|w| w.bits(buf.as_ptr() as u32)); + // MAXCNT must match SIZE + epout[i].maxcnt.write(|w| w.bits(size as u32)); + + dma_start(); + regs.events_endepout[i].reset(); + regs.tasks_startepout[i].write(|w| w.tasks_startepout().set_bit()); + while regs.events_endepout[i].read().events_endepout().bit_is_clear() {} + regs.events_endepout[i].reset(); + dma_end(); + + regs.size.epout[i].reset(); + + Ok(size) +} + +unsafe fn write_dma(i: usize, buf: &[u8]) { + let regs = T::regs(); + assert!(buf.len() <= 64); + + let mut ram_buf: MaybeUninit<[u8; 64]> = MaybeUninit::uninit(); + let ptr = if !slice_in_ram(buf) { + // EasyDMA can't read FLASH, so we copy through RAM + let ptr = ram_buf.as_mut_ptr() as *mut u8; + core::ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len()); + ptr + } else { + buf.as_ptr() + }; + + let epin = [ + ®s.epin0, + ®s.epin1, + ®s.epin2, + ®s.epin3, + ®s.epin4, + ®s.epin5, + ®s.epin6, + ®s.epin7, + ]; + + // Set the buffer length so the right number of bytes are transmitted. + // Safety: `buf.len()` has been checked to be <= the max buffer length. + epin[i].ptr.write(|w| w.bits(ptr as u32)); + epin[i].maxcnt.write(|w| w.maxcnt().bits(buf.len() as u8)); + + regs.events_endepin[i].reset(); + + dma_start(); + regs.tasks_startepin[i].write(|w| w.bits(1)); + while regs.events_endepin[i].read().bits() == 0 {} + dma_end(); +} + +impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + let i = self.info.addr.index(); + assert!(i != 0); + + self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; + + unsafe { read_dma::(i, buf) } + } +} + +impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + let i = self.info.addr.index(); + assert!(i != 0); + + self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; + + unsafe { write_dma::(i, buf) } + + Ok(()) + } +} + +/// USB control pipe. +pub struct ControlPipe<'d, T: Instance> { + _p: PeripheralRef<'d, T>, + max_packet_size: u16, +} + +impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { + fn max_packet_size(&self) -> usize { + usize::from(self.max_packet_size) + } + + async fn setup(&mut self) -> [u8; 8] { + let regs = T::regs(); + + // Reset shorts + regs.shorts.write(|w| w); + + // Wait for SETUP packet + regs.intenset.write(|w| w.ep0setup().set()); + poll_fn(|cx| { + EP0_WAKER.register(cx.waker()); + let regs = T::regs(); + if regs.events_ep0setup.read().bits() != 0 { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + regs.events_ep0setup.reset(); + + let mut buf = [0; 8]; + buf[0] = regs.bmrequesttype.read().bits() as u8; + buf[1] = regs.brequest.read().brequest().bits(); + buf[2] = regs.wvaluel.read().wvaluel().bits(); + buf[3] = regs.wvalueh.read().wvalueh().bits(); + buf[4] = regs.windexl.read().windexl().bits(); + buf[5] = regs.windexh.read().windexh().bits(); + buf[6] = regs.wlengthl.read().wlengthl().bits(); + buf[7] = regs.wlengthh.read().wlengthh().bits(); + + buf + } + + async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result { + let regs = T::regs(); + + regs.events_ep0datadone.reset(); + + // This starts a RX on EP0. events_ep0datadone notifies when done. + regs.tasks_ep0rcvout.write(|w| w.tasks_ep0rcvout().set_bit()); + + // Wait until ready + regs.intenset.write(|w| { + w.usbreset().set(); + w.ep0setup().set(); + w.ep0datadone().set() + }); + poll_fn(|cx| { + EP0_WAKER.register(cx.waker()); + let regs = T::regs(); + if regs.events_ep0datadone.read().bits() != 0 { + Poll::Ready(Ok(())) + } else if regs.events_usbreset.read().bits() != 0 { + trace!("aborted control data_out: usb reset"); + Poll::Ready(Err(EndpointError::Disabled)) + } else if regs.events_ep0setup.read().bits() != 0 { + trace!("aborted control data_out: received another SETUP"); + Poll::Ready(Err(EndpointError::Disabled)) + } else { + Poll::Pending + } + }) + .await?; + + unsafe { read_dma::(0, buf) } + } + + async fn data_in(&mut self, buf: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> { + let regs = T::regs(); + regs.events_ep0datadone.reset(); + + regs.shorts.write(|w| w.ep0datadone_ep0status().bit(last)); + + // This starts a TX on EP0. events_ep0datadone notifies when done. + unsafe { write_dma::(0, buf) } + + regs.intenset.write(|w| { + w.usbreset().set(); + w.ep0setup().set(); + w.ep0datadone().set() + }); + + poll_fn(|cx| { + cx.waker().wake_by_ref(); + EP0_WAKER.register(cx.waker()); + let regs = T::regs(); + if regs.events_ep0datadone.read().bits() != 0 { + Poll::Ready(Ok(())) + } else if regs.events_usbreset.read().bits() != 0 { + trace!("aborted control data_in: usb reset"); + Poll::Ready(Err(EndpointError::Disabled)) + } else if regs.events_ep0setup.read().bits() != 0 { + trace!("aborted control data_in: received another SETUP"); + Poll::Ready(Err(EndpointError::Disabled)) + } else { + Poll::Pending + } + }) + .await + } + + async fn accept(&mut self) { + let regs = T::regs(); + regs.tasks_ep0status.write(|w| w.tasks_ep0status().bit(true)); + } + + async fn reject(&mut self) { + let regs = T::regs(); + regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); + } + + async fn accept_set_address(&mut self, _addr: u8) { + self.accept().await; + // Nothing to do, the peripheral handles this. + } +} + +fn dma_start() { + compiler_fence(Ordering::Release); +} + +fn dma_end() { + compiler_fence(Ordering::Acquire); +} + +struct Allocator { + used: u16, +} + +impl Allocator { + fn new() -> Self { + Self { used: 0 } + } + + fn allocate(&mut self, ep_type: EndpointType) -> Result { + // Endpoint addresses are fixed in hardware: + // - 0x80 / 0x00 - Control EP0 + // - 0x81 / 0x01 - Bulk/Interrupt EP1 + // - 0x82 / 0x02 - Bulk/Interrupt EP2 + // - 0x83 / 0x03 - Bulk/Interrupt EP3 + // - 0x84 / 0x04 - Bulk/Interrupt EP4 + // - 0x85 / 0x05 - Bulk/Interrupt EP5 + // - 0x86 / 0x06 - Bulk/Interrupt EP6 + // - 0x87 / 0x07 - Bulk/Interrupt EP7 + // - 0x88 / 0x08 - Isochronous + + // Endpoint directions are allocated individually. + + let alloc_index = match ep_type { + EndpointType::Isochronous => 8, + EndpointType::Control => return Err(driver::EndpointAllocError), + EndpointType::Interrupt | EndpointType::Bulk => { + // Find rightmost zero bit in 1..=7 + let ones = (self.used >> 1).trailing_ones() as usize; + if ones >= 7 { + return Err(driver::EndpointAllocError); + } + ones + 1 + } + }; + + if self.used & (1 << alloc_index) != 0 { + return Err(driver::EndpointAllocError); + } + + self.used |= 1 << alloc_index; + + Ok(alloc_index) + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + fn regs() -> &'static pac::usbd::RegisterBlock; + } +} + +/// USB peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_usb { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::usb::sealed::Instance for peripherals::$type { + fn regs() -> &'static pac::usbd::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + } + impl crate::usb::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} + +mod errata { + + /// Writes `val` to `addr`. Used to apply Errata workarounds. + #[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "nrf52820"))] + unsafe fn poke(addr: u32, val: u32) { + (addr as *mut u32).write_volatile(val); + } + + /// Reads 32 bits from `addr`. + #[cfg(feature = "nrf52840")] + unsafe fn peek(addr: u32) -> u32 { + (addr as *mut u32).read_volatile() + } + + pub fn pre_enable() { + // Works around Erratum 187 on chip revisions 1 and 2. + #[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "nrf52820"))] + unsafe { + poke(0x4006EC00, 0x00009375); + poke(0x4006ED14, 0x00000003); + poke(0x4006EC00, 0x00009375); + } + + pre_wakeup(); + } + + pub fn post_enable() { + post_wakeup(); + + // Works around Erratum 187 on chip revisions 1 and 2. + #[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "nrf52820"))] + unsafe { + poke(0x4006EC00, 0x00009375); + poke(0x4006ED14, 0x00000000); + poke(0x4006EC00, 0x00009375); + } + } + + pub fn pre_wakeup() { + // Works around Erratum 171 on chip revisions 1 and 2. + + #[cfg(feature = "nrf52840")] + unsafe { + if peek(0x4006EC00) == 0x00000000 { + poke(0x4006EC00, 0x00009375); + } + + poke(0x4006EC14, 0x000000C0); + poke(0x4006EC00, 0x00009375); + } + } + + pub fn post_wakeup() { + // Works around Erratum 171 on chip revisions 1 and 2. + + #[cfg(feature = "nrf52840")] + unsafe { + if peek(0x4006EC00) == 0x00000000 { + poke(0x4006EC00, 0x00009375); + } + + poke(0x4006EC14, 0x00000000); + poke(0x4006EC00, 0x00009375); + } + } +} diff --git a/embassy-nrf/src/usb/vbus_detect.rs b/embassy-nrf/src/usb/vbus_detect.rs new file mode 100644 index 000000000..cecd4c595 --- /dev/null +++ b/embassy-nrf/src/usb/vbus_detect.rs @@ -0,0 +1,177 @@ +//! Trait and implementations for performing VBUS detection. + +use core::future::poll_fn; +use core::sync::atomic::{AtomicBool, Ordering}; +use core::task::Poll; + +use embassy_sync::waitqueue::AtomicWaker; + +use super::BUS_WAKER; +use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::pac; + +/// Trait for detecting USB VBUS power. +/// +/// There are multiple ways to detect USB power. The behavior +/// here provides a hook into determining whether it is. +pub trait VbusDetect { + /// Report whether power is detected. + /// + /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the + /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. + fn is_usb_detected(&self) -> bool; + + /// Wait until USB power is ready. + /// + /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the + /// `USBPWRRDY` event from the `POWER` peripheral. + async fn wait_power_ready(&mut self) -> Result<(), ()>; +} + +#[cfg(not(feature = "_nrf5340"))] +type UsbRegIrq = interrupt::POWER_CLOCK; +#[cfg(feature = "_nrf5340")] +type UsbRegIrq = interrupt::USBREGULATOR; + +#[cfg(not(feature = "_nrf5340"))] +type UsbRegPeri = pac::POWER; +#[cfg(feature = "_nrf5340")] +type UsbRegPeri = pac::USBREGULATOR; + +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = unsafe { &*UsbRegPeri::ptr() }; + + if regs.events_usbdetected.read().bits() != 0 { + regs.events_usbdetected.reset(); + BUS_WAKER.wake(); + } + + if regs.events_usbremoved.read().bits() != 0 { + regs.events_usbremoved.reset(); + BUS_WAKER.wake(); + POWER_WAKER.wake(); + } + + if regs.events_usbpwrrdy.read().bits() != 0 { + regs.events_usbpwrrdy.reset(); + POWER_WAKER.wake(); + } + } +} + +/// [`VbusDetect`] implementation using the native hardware POWER peripheral. +/// +/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces +/// to POWER. In that case, use [`VbusDetectSignal`]. +pub struct HardwareVbusDetect { + _private: (), +} + +static POWER_WAKER: AtomicWaker = AtomicWaker::new(); + +impl HardwareVbusDetect { + /// Create a new `VbusDetectNative`. + pub fn new(_irq: impl interrupt::Binding + 'static) -> Self { + let regs = unsafe { &*UsbRegPeri::ptr() }; + + unsafe { UsbRegIrq::steal() }.unpend(); + unsafe { UsbRegIrq::steal() }.enable(); + + regs.intenset + .write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set()); + + Self { _private: () } + } +} + +impl VbusDetect for HardwareVbusDetect { + fn is_usb_detected(&self) -> bool { + let regs = unsafe { &*UsbRegPeri::ptr() }; + regs.usbregstatus.read().vbusdetect().is_vbus_present() + } + + async fn wait_power_ready(&mut self) -> Result<(), ()> { + poll_fn(move |cx| { + POWER_WAKER.register(cx.waker()); + let regs = unsafe { &*UsbRegPeri::ptr() }; + + if regs.usbregstatus.read().outputrdy().is_ready() { + Poll::Ready(Ok(())) + } else if !self.is_usb_detected() { + Poll::Ready(Err(())) + } else { + Poll::Pending + } + }) + .await + } +} + +/// Software-backed [`VbusDetect`] implementation. +/// +/// This implementation does not interact with the hardware, it allows user code +/// to notify the power events by calling functions instead. +/// +/// This is suitable for use with the nRF softdevice, by calling the functions +/// when the softdevice reports power-related events. +pub struct SoftwareVbusDetect { + usb_detected: AtomicBool, + power_ready: AtomicBool, +} + +impl SoftwareVbusDetect { + /// Create a new `SoftwareVbusDetect`. + pub fn new(usb_detected: bool, power_ready: bool) -> Self { + BUS_WAKER.wake(); + + Self { + usb_detected: AtomicBool::new(usb_detected), + power_ready: AtomicBool::new(power_ready), + } + } + + /// Report whether power was detected. + /// + /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. + pub fn detected(&self, detected: bool) { + self.usb_detected.store(detected, Ordering::Relaxed); + self.power_ready.store(false, Ordering::Relaxed); + BUS_WAKER.wake(); + POWER_WAKER.wake(); + } + + /// Report when USB power is ready. + /// + /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. + pub fn ready(&self) { + self.power_ready.store(true, Ordering::Relaxed); + POWER_WAKER.wake(); + } +} + +impl VbusDetect for &SoftwareVbusDetect { + fn is_usb_detected(&self) -> bool { + self.usb_detected.load(Ordering::Relaxed) + } + + async fn wait_power_ready(&mut self) -> Result<(), ()> { + poll_fn(move |cx| { + POWER_WAKER.register(cx.waker()); + + if self.power_ready.load(Ordering::Relaxed) { + Poll::Ready(Ok(())) + } else if !self.usb_detected.load(Ordering::Relaxed) { + Poll::Ready(Err(())) + } else { + Poll::Pending + } + }) + .await + } +} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index cfdda076e..cc88d92c7 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,7 +6,6 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] -msos-descriptor = ["embassy-usb/msos-descriptor"] nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lorawan-device", "lorawan"] @@ -17,7 +16,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } @@ -36,7 +35,3 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } - -[[bin]] -name = "usb_serial_winusb" -required-features = ["msos-descriptor"] diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 083a1cbb0..b8a72313a 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -9,8 +9,9 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{bind_interrupts, interrupt, pac, peripherals, rng}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, rng, usb}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; @@ -19,6 +20,8 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; RNG => rng::InterruptHandler; }); @@ -60,9 +63,7 @@ async fn main(spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -86,6 +87,7 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], + &mut singleton!([0; 128])[..], ); // Our MAC addr. diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 3d8a114cd..7ccd2946a 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -10,8 +10,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; @@ -20,6 +21,11 @@ use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + static SUSPENDED: AtomicBool = AtomicBool::new(false); #[embassy_executor::main] @@ -32,9 +38,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -50,6 +54,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; let mut device_handler = MyDeviceHandler::new(); @@ -62,6 +67,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index d7c9d55b7..edf634a5e 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -7,8 +7,9 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_time::{Duration, Timer}; use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; @@ -16,6 +17,11 @@ use embassy_usb::{Builder, Config}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -26,9 +32,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -43,6 +47,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; @@ -54,6 +59,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 102d7ea60..9727a4f57 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -7,13 +7,19 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; +use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -24,9 +30,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -48,6 +52,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let mut state = State::new(); @@ -58,6 +63,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 558d4ba60..6da2c2a2f 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -6,14 +6,29 @@ use core::mem; use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config, UsbDevice}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; #[embassy_executor::task] @@ -39,10 +54,9 @@ async fn main(spawner: Spawner) { info!("Enabling ext hfosc..."); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); while clock.events_hfclkstarted.read().bits() != 1 {} + // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -59,34 +73,21 @@ async fn main(spawner: Spawner) { config.device_protocol = 0x01; config.composite_with_iads = true; - struct Resources { - device_descriptor: [u8; 256], - config_descriptor: [u8; 256], - bos_descriptor: [u8; 256], - control_buf: [u8; 64], - serial_state: State<'static>, - } - static RESOURCES: StaticCell = StaticCell::new(); - let res = RESOURCES.init(Resources { - device_descriptor: [0; 256], - config_descriptor: [0; 256], - bos_descriptor: [0; 256], - control_buf: [0; 64], - serial_state: State::new(), - }); + let state = singleton!(State::new()); // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut res.device_descriptor, - &mut res.config_descriptor, - &mut res.bos_descriptor, - &mut res.control_buf, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], + &mut singleton!([0; 128])[..], ); // Create classes on the builder. - let class = CdcAcmClass::new(&mut builder, &mut res.serial_state, 64); + let class = CdcAcmClass::new(&mut builder, state, 64); // Build the builder. let usb = builder.build(); diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index 6561fc3b4..6e4f71a48 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -7,8 +7,9 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; +use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::msos::{self, windows_version}; @@ -16,6 +17,11 @@ use embassy_usb::types::InterfaceNumber; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + // This is a randomly generated GUID to allow clients on Windows to find our device const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; @@ -29,9 +35,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); -- cgit