From c46e9b6cfc1375f5e5e2bade8eb2174cc47c4a28 Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 15 Jun 2022 12:17:04 +1000 Subject: [PATCH 1/5] Introduces EnabledUsbDevice EnabledUsbDevice is a wrapper around the UsbDevice where their enablement is also subject to external events, such as POWER events for nRF. It is introduced generically to support other platforms should they also require external signalling for enablement. --- embassy-usb/src/lib.rs | 1 + embassy-usb/src/util.rs | 68 ++++++++++++++++++++++++++++++ examples/nrf/src/bin/usb_serial.rs | 38 ++++++++++++++--- 3 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 embassy-usb/src/util.rs diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index f2577d4fc..eba46b892 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -11,6 +11,7 @@ pub mod descriptor; mod descriptor_reader; pub mod driver; pub mod types; +pub mod util; use embassy::util::{select, Either}; use heapless::Vec; diff --git a/embassy-usb/src/util.rs b/embassy-usb/src/util.rs new file mode 100644 index 000000000..ac56691b8 --- /dev/null +++ b/embassy-usb/src/util.rs @@ -0,0 +1,68 @@ +use embassy::channel::signal::Signal; +use embassy::util::{select, Either}; + +use crate::driver::Driver; +use crate::UsbDevice; + +/// Am enabled usb device is a device that further receives external notifications +/// regarding whether it is enabled or not. A common example of where this is +/// required is when receiving notifications from the POWER peripheral that +/// USB has been connected to or removed. The device here wraps an existing +/// USB device, keeping it publically available so that device-oriented operations +/// may still be performed. A signal is also provided that enables/disables the +/// USB device, taking care of suspension and resumption. In the case of the POWER +/// peripheral, this signal can be used from within a POWER_CLOCK interrupt +/// handler. Alternatively, for softdevice usage where the POWER peripheral is not +/// available, similar USB power events can be leveraged. +pub struct EnabledUsbDevice<'d, D: Driver<'d>> { + pub underlying: UsbDevice<'d, D>, + enable_usb_signal: &'d Signal, +} + +impl<'d, D: Driver<'d>> EnabledUsbDevice<'d, D> { + /// Wrap an existing UsbDevice and take a signal that will be used + /// to enable/disable it, perhaps from an external POWER_CLOCK + /// interrupt, or the equivalent when dealing with softdevices. + pub fn new(underlying: UsbDevice<'d, D>, enable_usb_signal: &'d Signal) -> Self { + Self { + underlying, + enable_usb_signal, + } + } + + /// Runs the underlying `UsbDevice` taking care of reacting to USB becoming + /// enabled/disabled. + /// + /// This future may leave the bus in an invalid state if it is dropped. + /// After dropping the future, [`UsbDevice::disable()`] should be called + /// before calling any other `UsbDevice` methods to fully reset the + /// peripheral. + pub async fn run(&mut self) -> ! { + while !self.enable_usb_signal.wait().await {} + loop { + match select( + self.underlying.run_until_suspend(), + self.enable_usb_signal.wait(), + ) + .await + { + Either::First(_) => {} + Either::Second(enable) => { + if !enable { + self.underlying.disable().await; + while !self.enable_usb_signal.wait().await {} + } + } + } + match select(self.underlying.wait_resume(), self.enable_usb_signal.wait()).await { + Either::First(_) => {} + Either::Second(enable) => { + if !enable { + self.underlying.disable().await; + while !self.enable_usb_signal.wait().await {} + } + } + } + } + } +} diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index f108db46d..377ae8c3a 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -6,28 +6,52 @@ use core::mem; use defmt::{info, panic}; +use embassy::channel::signal::Signal; use embassy::executor::Spawner; +use embassy::interrupt::InterruptExt; use embassy_nrf::usb::{Driver, Instance}; -use embassy_nrf::{interrupt, pac, Peripherals}; +use embassy_nrf::{interrupt, interrupt, pac, pac, Peripherals}; use embassy_usb::driver::EndpointError; +use embassy_usb::util::EnabledUsbDevice; use embassy_usb::{Builder, Config}; use embassy_usb_serial::{CdcAcmClass, State}; use futures::future::join; -use {defmt_rtt as _, panic_probe as _}; +use {defmt_rtt as _, panic_probe as _}; // global logger + +static ENABLE_USB: Signal = Signal::new(); + +fn on_power_interrupt(_: *mut ()) { + let regs = unsafe { &*pac::POWER::ptr() }; + + if regs.events_usbdetected.read().bits() != 0 { + regs.events_usbdetected.reset(); + info!("Vbus detected, enabling USB..."); + ENABLE_USB.signal(true); + } + + if regs.events_usbremoved.read().bits() != 0 { + regs.events_usbremoved.reset(); + info!("Vbus removed, disabling USB..."); + ENABLE_USB.signal(false); + } +} #[embassy::main] async fn main(_spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; let power: pac::POWER = unsafe { mem::transmute(()) }; + let power_irq = interrupt::take!(POWER_CLOCK); + power_irq.set_handler(on_power_interrupt); + power_irq.unpend(); + power_irq.enable(); + + power.intenset.write(|w| w.usbdetected().set().usbremoved().set()); + info!("Enabling ext hfosc..."); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); while clock.events_hfclkstarted.read().bits() != 1 {} - info!("Waiting for vbus..."); - while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} - info!("vbus OK"); - // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let driver = Driver::new(p.USBD, irq); @@ -70,7 +94,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); // Build the builder. - let mut usb = builder.build(); + let mut usb = EnabledUsbDevice::new(builder.build(), &ENABLE_USB); // Run the USB device. let usb_fut = usb.run(); From 4a8f117f2520df9d1919cbbac3d2840ea1539e04 Mon Sep 17 00:00:00 2001 From: huntc Date: Thu, 16 Jun 2022 16:08:58 +1000 Subject: [PATCH 2/5] Puts in the machinery to handle power detected/removed --- embassy-nrf/src/usb.rs | 70 ++++++++++++++++++++++-- embassy-stm32/src/usb/usb.rs | 87 +++++++++++++++++------------- embassy-usb/src/driver.rs | 6 +++ embassy-usb/src/lib.rs | 20 +++++-- embassy-usb/src/util.rs | 68 ----------------------- examples/nrf/src/bin/usb_serial.rs | 38 ++----------- 6 files changed, 144 insertions(+), 145 deletions(-) delete mode 100644 embassy-usb/src/util.rs diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 6c872581d..88af74fc0 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; use core::mem::MaybeUninit; -use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; +use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; use core::task::Poll; use cortex_m::peripheral::NVIC; @@ -25,6 +25,7 @@ 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); +static POWER_AVAILABLE: AtomicBool = AtomicBool::new(false); pub struct Driver<'d, T: Instance> { phantom: PhantomData<&'d mut T>, @@ -39,6 +40,11 @@ impl<'d, T: Instance> Driver<'d, T> { irq.unpend(); irq.enable(); + // Initialize the bus so that it signals that power is available. + // Not required when using with_power_management as we then rely on the irq. + POWER_AVAILABLE.store(true, Ordering::Relaxed); + BUS_WAKER.wake(); + Self { phantom: PhantomData, alloc_in: Allocator::new(), @@ -46,6 +52,25 @@ impl<'d, T: Instance> Driver<'d, T> { } } + /// Establish a new device that then uses the POWER peripheral to + /// detect USB power detected/removed events are handled. + #[cfg(not(feature = "_nrf5340-app"))] + pub fn with_power_management( + _usb: impl Unborrow + 'd, + irq: impl Unborrow + 'd, + power_irq: impl Interrupt, + ) -> Self { + let regs = unsafe { &*pac::POWER::ptr() }; + + power_irq.set_handler(Self::on_power_interrupt); + power_irq.unpend(); + power_irq.enable(); + + regs.intenset.write(|w| w.usbdetected().set().usbremoved().set()); + + Self::new(_usb, irq) + } + fn on_interrupt(_: *mut ()) { let regs = T::regs(); @@ -76,7 +101,6 @@ impl<'d, T: Instance> Driver<'d, T> { // doesn't cause an infinite irq loop. if regs.events_usbevent.read().bits() != 0 { regs.events_usbevent.reset(); - //regs.intenclr.write(|w| w.usbevent().clear()); BUS_WAKER.wake(); } @@ -96,6 +120,31 @@ impl<'d, T: Instance> Driver<'d, T> { } } } + + #[cfg(not(feature = "_nrf5340-app"))] + fn on_power_interrupt(_: *mut ()) { + let regs = unsafe { &*pac::POWER::ptr() }; + + let mut power_changed = false; + let mut power_available = false; + + if regs.events_usbdetected.read().bits() != 0 { + regs.events_usbdetected.reset(); + power_changed = true; + power_available = true; + } + + if regs.events_usbremoved.read().bits() != 0 { + regs.events_usbremoved.reset(); + power_changed = true; + power_available = false; + } + + if power_changed { + POWER_AVAILABLE.store(power_available, Ordering::Relaxed); + BUS_WAKER.wake(); + } + } } impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { @@ -138,7 +187,10 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { ( - Bus { phantom: PhantomData }, + Bus { + phantom: PhantomData, + power_available: false, + }, ControlPipe { _phantom: PhantomData, max_packet_size: control_max_packet_size, @@ -149,6 +201,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { pub struct Bus<'d, T: Instance> { phantom: PhantomData<&'d mut T>, + power_available: bool, } impl<'d, T: Instance> driver::Bus for Bus<'d, T> { @@ -246,6 +299,17 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { trace!("USB event: ready"); } + if POWER_AVAILABLE.load(Ordering::Relaxed) != 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 }) } diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index cadbb423c..4633ff00c 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -161,6 +161,9 @@ impl<'d, T: Instance> Driver<'d, T> { dm.set_as_af(dm.af_num(), AFType::OutputPushPull); } + // Initialize the bus so that it signals that power is available + BUS_WAKER.wake(); + Self { phantom: PhantomData, alloc: [EndpointData { @@ -406,6 +409,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { Bus { phantom: PhantomData, ep_types, + inited: false, }, ControlPipe { _phantom: PhantomData, @@ -420,6 +424,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { pub struct Bus<'d, T: Instance> { phantom: PhantomData<&'d mut T>, ep_types: [EpType; EP_COUNT - 1], + inited: bool, } impl<'d, T: Instance> driver::Bus for Bus<'d, T> { @@ -428,53 +433,59 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> { poll_fn(move |cx| unsafe { BUS_WAKER.register(cx.waker()); - let regs = T::regs(); - let flags = IRQ_FLAGS.load(Ordering::Acquire); + if self.inited { + let regs = T::regs(); - if flags & IRQ_FLAG_RESUME != 0 { - IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel); - return Poll::Ready(Event::Resume); - } + let flags = IRQ_FLAGS.load(Ordering::Acquire); - if flags & IRQ_FLAG_RESET != 0 { - IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel); - - trace!("RESET REGS WRITINGINGING"); - regs.daddr().write(|w| { - w.set_ef(true); - w.set_add(0); - }); - - regs.epr(0).write(|w| { - w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat::NAK); - w.set_stat_tx(Stat::NAK); - }); - - for i in 1..EP_COUNT { - regs.epr(i).write(|w| { - w.set_ea(i as _); - w.set_ep_type(self.ep_types[i - 1]); - }) + if flags & IRQ_FLAG_RESUME != 0 { + IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel); + return Poll::Ready(Event::Resume); } - for w in &EP_IN_WAKERS { - w.wake() - } - for w in &EP_OUT_WAKERS { - w.wake() + if flags & IRQ_FLAG_RESET != 0 { + IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel); + + trace!("RESET REGS WRITINGINGING"); + regs.daddr().write(|w| { + w.set_ef(true); + w.set_add(0); + }); + + regs.epr(0).write(|w| { + w.set_ep_type(EpType::CONTROL); + w.set_stat_rx(Stat::NAK); + w.set_stat_tx(Stat::NAK); + }); + + for i in 1..EP_COUNT { + regs.epr(i).write(|w| { + w.set_ea(i as _); + w.set_ep_type(self.ep_types[i - 1]); + }) + } + + for w in &EP_IN_WAKERS { + w.wake() + } + for w in &EP_OUT_WAKERS { + w.wake() + } + + return Poll::Ready(Event::Reset); } - return Poll::Ready(Event::Reset); - } + if flags & IRQ_FLAG_SUSPEND != 0 { + IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel); + return Poll::Ready(Event::Suspend); + } - if flags & IRQ_FLAG_SUSPEND != 0 { - IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel); - return Poll::Ready(Event::Suspend); + Poll::Pending + } else { + self.inited = true; + return Poll::Ready(Event::PowerDetected); } - - Poll::Pending }) } diff --git a/embassy-usb/src/driver.rs b/embassy-usb/src/driver.rs index bfe44d627..2a84ff9e7 100644 --- a/embassy-usb/src/driver.rs +++ b/embassy-usb/src/driver.rs @@ -202,6 +202,12 @@ pub enum Event { /// A USB resume request has been detected after being suspended or, in the case of self-powered /// devices, the device has been connected to the USB bus. Resume, + + /// The USB power has been detected. + PowerDetected, + + /// The USB power has been removed. Not supported by all devices. + PowerRemoved, } #[derive(Copy, Clone, Eq, PartialEq, Debug)] diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index eba46b892..af102e589 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -11,7 +11,6 @@ pub mod descriptor; mod descriptor_reader; pub mod driver; pub mod types; -pub mod util; use embassy::util::{select, Either}; use heapless::Vec; @@ -115,6 +114,7 @@ struct Inner<'d, D: Driver<'d>> { device_state: UsbDeviceState, suspended: bool, + power_available: bool, remote_wakeup_enabled: bool, self_powered: bool, @@ -157,6 +157,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { device_state: UsbDeviceState::Disabled, suspended: false, + power_available: false, remote_wakeup_enabled: false, self_powered: false, address: 0, @@ -186,6 +187,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { /// before calling any other `UsbDevice` methods to fully reset the /// peripheral. pub async fn run_until_suspend(&mut self) -> () { + while !self.inner.power_available { + let evt = self.inner.bus.poll().await; + self.inner.handle_bus_event(evt); + } + if self.inner.device_state == UsbDeviceState::Disabled { self.inner.bus.enable().await; self.inner.device_state = UsbDeviceState::Default; @@ -195,7 +201,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { } } - while !self.inner.suspended { + while !self.inner.suspended && self.inner.power_available { let control_fut = self.control.setup(); let bus_fut = self.inner.bus.poll(); match select(bus_fut, control_fut).await { @@ -223,7 +229,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { /// /// This future is cancel-safe. pub async fn wait_resume(&mut self) { - while self.inner.suspended { + while self.inner.suspended || !self.inner.power_available { let evt = self.inner.bus.poll().await; self.inner.handle_bus_event(evt); } @@ -377,6 +383,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { h.suspended(true); } } + Event::PowerDetected => { + trace!("usb: power detected"); + self.power_available = true; + } + Event::PowerRemoved => { + trace!("usb: power removed"); + self.power_available = false; + } } } diff --git a/embassy-usb/src/util.rs b/embassy-usb/src/util.rs deleted file mode 100644 index ac56691b8..000000000 --- a/embassy-usb/src/util.rs +++ /dev/null @@ -1,68 +0,0 @@ -use embassy::channel::signal::Signal; -use embassy::util::{select, Either}; - -use crate::driver::Driver; -use crate::UsbDevice; - -/// Am enabled usb device is a device that further receives external notifications -/// regarding whether it is enabled or not. A common example of where this is -/// required is when receiving notifications from the POWER peripheral that -/// USB has been connected to or removed. The device here wraps an existing -/// USB device, keeping it publically available so that device-oriented operations -/// may still be performed. A signal is also provided that enables/disables the -/// USB device, taking care of suspension and resumption. In the case of the POWER -/// peripheral, this signal can be used from within a POWER_CLOCK interrupt -/// handler. Alternatively, for softdevice usage where the POWER peripheral is not -/// available, similar USB power events can be leveraged. -pub struct EnabledUsbDevice<'d, D: Driver<'d>> { - pub underlying: UsbDevice<'d, D>, - enable_usb_signal: &'d Signal, -} - -impl<'d, D: Driver<'d>> EnabledUsbDevice<'d, D> { - /// Wrap an existing UsbDevice and take a signal that will be used - /// to enable/disable it, perhaps from an external POWER_CLOCK - /// interrupt, or the equivalent when dealing with softdevices. - pub fn new(underlying: UsbDevice<'d, D>, enable_usb_signal: &'d Signal) -> Self { - Self { - underlying, - enable_usb_signal, - } - } - - /// Runs the underlying `UsbDevice` taking care of reacting to USB becoming - /// enabled/disabled. - /// - /// This future may leave the bus in an invalid state if it is dropped. - /// After dropping the future, [`UsbDevice::disable()`] should be called - /// before calling any other `UsbDevice` methods to fully reset the - /// peripheral. - pub async fn run(&mut self) -> ! { - while !self.enable_usb_signal.wait().await {} - loop { - match select( - self.underlying.run_until_suspend(), - self.enable_usb_signal.wait(), - ) - .await - { - Either::First(_) => {} - Either::Second(enable) => { - if !enable { - self.underlying.disable().await; - while !self.enable_usb_signal.wait().await {} - } - } - } - match select(self.underlying.wait_resume(), self.enable_usb_signal.wait()).await { - Either::First(_) => {} - Either::Second(enable) => { - if !enable { - self.underlying.disable().await; - while !self.enable_usb_signal.wait().await {} - } - } - } - } - } -} diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index 377ae8c3a..7c1d8cbb8 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -6,47 +6,18 @@ use core::mem; use defmt::{info, panic}; -use embassy::channel::signal::Signal; use embassy::executor::Spawner; -use embassy::interrupt::InterruptExt; use embassy_nrf::usb::{Driver, Instance}; -use embassy_nrf::{interrupt, interrupt, pac, pac, Peripherals}; +use embassy_nrf::{interrupt, pac, Peripherals}; use embassy_usb::driver::EndpointError; -use embassy_usb::util::EnabledUsbDevice; use embassy_usb::{Builder, Config}; use embassy_usb_serial::{CdcAcmClass, State}; use futures::future::join; -use {defmt_rtt as _, panic_probe as _}; // global logger - -static ENABLE_USB: Signal = Signal::new(); - -fn on_power_interrupt(_: *mut ()) { - let regs = unsafe { &*pac::POWER::ptr() }; - - if regs.events_usbdetected.read().bits() != 0 { - regs.events_usbdetected.reset(); - info!("Vbus detected, enabling USB..."); - ENABLE_USB.signal(true); - } - - if regs.events_usbremoved.read().bits() != 0 { - regs.events_usbremoved.reset(); - info!("Vbus removed, disabling USB..."); - ENABLE_USB.signal(false); - } -} +use {defmt_rtt as _, panic_probe as _}; #[embassy::main] async fn main(_spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; - let power: pac::POWER = unsafe { mem::transmute(()) }; - - let power_irq = interrupt::take!(POWER_CLOCK); - power_irq.set_handler(on_power_interrupt); - power_irq.unpend(); - power_irq.enable(); - - power.intenset.write(|w| w.usbdetected().set().usbremoved().set()); info!("Enabling ext hfosc..."); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); @@ -54,7 +25,8 @@ async fn main(_spawner: Spawner, p: Peripherals) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); - let driver = Driver::new(p.USBD, irq); + let power_irq = interrupt::take!(POWER_CLOCK); + let driver = Driver::with_power_management(p.USBD, irq, power_irq); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -94,7 +66,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); // Build the builder. - let mut usb = EnabledUsbDevice::new(builder.build(), &ENABLE_USB); + let mut usb = builder.build(); // Run the USB device. let usb_fut = usb.run(); From 8d71a358c8fa0524fce564357d3d36f24be41353 Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 8 Jul 2022 15:30:15 +1000 Subject: [PATCH 3/5] Build in a new Unpowered state Replaces the sub-state of representing being being available. Power states also now set enable/disable directly too, which simplifies code. --- embassy-usb/src/lib.rs | 45 ++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index af102e589..82ff4e32f 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -30,6 +30,9 @@ use crate::driver::ControlPipe; #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum UsbDeviceState { + /// The USB device has no power. + Unpowered, + /// The USB device is disabled. Disabled, @@ -114,7 +117,6 @@ struct Inner<'d, D: Driver<'d>> { device_state: UsbDeviceState, suspended: bool, - power_available: bool, remote_wakeup_enabled: bool, self_powered: bool, @@ -155,9 +157,8 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { config_descriptor, bos_descriptor, - device_state: UsbDeviceState::Disabled, + device_state: UsbDeviceState::Unpowered, suspended: false, - power_available: false, remote_wakeup_enabled: false, self_powered: false, address: 0, @@ -187,25 +188,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { /// before calling any other `UsbDevice` methods to fully reset the /// peripheral. pub async fn run_until_suspend(&mut self) -> () { - while !self.inner.power_available { - let evt = self.inner.bus.poll().await; - self.inner.handle_bus_event(evt); - } - - if self.inner.device_state == UsbDeviceState::Disabled { - self.inner.bus.enable().await; - self.inner.device_state = UsbDeviceState::Default; - - if let Some(h) = &self.inner.handler { - h.enabled(true); - } - } - - while !self.inner.suspended && self.inner.power_available { + while !self.inner.suspended { let control_fut = self.control.setup(); let bus_fut = self.inner.bus.poll(); match select(bus_fut, control_fut).await { - Either::First(evt) => self.inner.handle_bus_event(evt), + Either::First(evt) => self.inner.handle_bus_event(evt).await, Either::Second(req) => self.handle_control(req).await, } } @@ -229,9 +216,9 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { /// /// This future is cancel-safe. pub async fn wait_resume(&mut self) { - while self.inner.suspended || !self.inner.power_available { + while self.inner.suspended { let evt = self.inner.bus.poll().await; - self.inner.handle_bus_event(evt); + self.inner.handle_bus_event(evt).await; } } @@ -348,7 +335,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { } impl<'d, D: Driver<'d>> Inner<'d, D> { - fn handle_bus_event(&mut self, evt: Event) { + async fn handle_bus_event(&mut self, evt: Event) { match evt { Event::Reset => { trace!("usb: reset"); @@ -385,11 +372,21 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { } Event::PowerDetected => { trace!("usb: power detected"); - self.power_available = true; + self.bus.enable().await; + self.device_state = UsbDeviceState::Default; + + if let Some(h) = &self.handler { + h.enabled(true); + } } Event::PowerRemoved => { trace!("usb: power removed"); - self.power_available = false; + self.bus.disable().await; + self.device_state = UsbDeviceState::Unpowered; + + if let Some(h) = &self.handler { + h.enabled(false); + } } } } From 81796d29b44f1b052d7631bc8677525d5aa70b50 Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 8 Jul 2022 16:22:25 +1000 Subject: [PATCH 4/5] New constructor to cater for the softdevice Also, correctly sets the initial power management state when using power management --- embassy-nrf/src/usb.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 88af74fc0..973fe16f8 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -35,6 +35,17 @@ pub struct Driver<'d, T: Instance> { impl<'d, T: Instance> Driver<'d, T> { pub fn new(_usb: impl Unborrow + 'd, irq: impl Unborrow + 'd) -> Self { + Self::with_power_state(_usb, irq, true) + } + + /// Establish a new device that then puts the USB device into an initial power state. + /// Required when using the nRF softdevice where power is unavailable until + /// notified by it, at which time the the [`Self::power()`] method should then be called. + pub fn with_power_state( + _usb: impl Unborrow + 'd, + irq: impl Unborrow + 'd, + power_available: bool, + ) -> Self { unborrow!(irq); irq.set_handler(Self::on_interrupt); irq.unpend(); @@ -42,7 +53,7 @@ impl<'d, T: Instance> Driver<'d, T> { // Initialize the bus so that it signals that power is available. // Not required when using with_power_management as we then rely on the irq. - POWER_AVAILABLE.store(true, Ordering::Relaxed); + POWER_AVAILABLE.store(power_available, Ordering::Relaxed); BUS_WAKER.wake(); Self { @@ -68,7 +79,7 @@ impl<'d, T: Instance> Driver<'d, T> { regs.intenset.write(|w| w.usbdetected().set().usbremoved().set()); - Self::new(_usb, irq) + Self::with_power_state(_usb, irq, regs.usbregstatus.read().vbusdetect().is_vbus_present()) } fn on_interrupt(_: *mut ()) { @@ -145,6 +156,14 @@ impl<'d, T: Instance> Driver<'d, T> { BUS_WAKER.wake(); } } + + /// Manually declare that USB power is available or unavailable. + /// Useful in scenarios where power management cannot be managed + /// automatically e.g. when dealing with the nrf-softdevice. + pub fn power(available: bool) { + POWER_AVAILABLE.store(available, Ordering::Relaxed); + BUS_WAKER.wake(); + } } impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { From 8785fbc6f1a1227115d3ffa6a6c19035bed6ef8c Mon Sep 17 00:00:00 2001 From: huntc Date: Sat, 9 Jul 2022 16:40:10 +1000 Subject: [PATCH 5/5] Trait for UsbSupply Eliminated a signal by using a simpler trait method that returns whether VBus power is available. Also includes a UsbSupply that can be signalled for use with the nRF softdevice. Includes the requirement for waiting for power to become available. --- embassy-nrf/src/usb.rs | 224 ++++++++++++------- examples/nrf/src/bin/usb_ethernet.rs | 12 +- examples/nrf/src/bin/usb_hid_keyboard.rs | 71 +----- examples/nrf/src/bin/usb_hid_mouse.rs | 10 +- examples/nrf/src/bin/usb_serial.rs | 8 +- examples/nrf/src/bin/usb_serial_multitask.rs | 13 +- 6 files changed, 173 insertions(+), 165 deletions(-) diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 973fe16f8..9b6e6e83d 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -25,63 +25,163 @@ 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); -static POWER_AVAILABLE: AtomicBool = AtomicBool::new(false); -pub struct Driver<'d, T: Instance> { +/// There are multiple ways to detect USB power. The behavior +/// here provides a hook into determining whether it is. +pub trait UsbSupply { + fn is_usb_detected(&self) -> bool; + + type UsbPowerReadyFuture<'a>: Future> + 'a + where + Self: 'a; + fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_>; +} + +pub struct Driver<'d, T: Instance, P: UsbSupply> { phantom: PhantomData<&'d mut T>, alloc_in: Allocator, alloc_out: Allocator, + usb_supply: P, } -impl<'d, T: Instance> Driver<'d, T> { - pub fn new(_usb: impl Unborrow + 'd, irq: impl Unborrow + 'd) -> Self { - Self::with_power_state(_usb, irq, true) +/// Uses the POWER peripheral to detect when power is available +/// for USB. Unsuitable for usage with the nRF softdevice. +#[cfg(not(feature = "_nrf5340-app"))] +pub struct PowerUsb {} + +/// Can be used to signal that power is available. Particularly suited for +/// use with the nRF softdevice. +pub struct SignalledSupply { + usb_detected: AtomicBool, + power_ready: AtomicBool, +} + +static POWER_WAKER: AtomicWaker = NEW_AW; + +#[cfg(not(feature = "_nrf5340-app"))] +impl PowerUsb { + 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 {} } - /// Establish a new device that then puts the USB device into an initial power state. - /// Required when using the nRF softdevice where power is unavailable until - /// notified by it, at which time the the [`Self::power()`] method should then be called. - pub fn with_power_state( + #[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 UsbSupply for PowerUsb { + fn is_usb_detected(&self) -> bool { + let regs = unsafe { &*pac::POWER::ptr() }; + regs.usbregstatus.read().vbusdetect().is_vbus_present() + } + + type UsbPowerReadyFuture<'a> = impl Future> + 'a where Self: 'a; + fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_> { + 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 + } + }) + } +} + +impl SignalledSupply { + 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), + } + } + + 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(); + } + + pub fn ready(&self) { + self.power_ready.store(true, Ordering::Relaxed); + POWER_WAKER.wake(); + } +} + +impl UsbSupply for SignalledSupply { + fn is_usb_detected(&self) -> bool { + self.usb_detected.load(Ordering::Relaxed) + } + + type UsbPowerReadyFuture<'a> = impl Future> + 'a where Self: 'a; + fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_> { + 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 + } + }) + } +} + +impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> { + pub fn new( _usb: impl Unborrow + 'd, irq: impl Unborrow + 'd, - power_available: bool, + usb_supply: P, ) -> Self { unborrow!(irq); irq.set_handler(Self::on_interrupt); irq.unpend(); irq.enable(); - // Initialize the bus so that it signals that power is available. - // Not required when using with_power_management as we then rely on the irq. - POWER_AVAILABLE.store(power_available, Ordering::Relaxed); - BUS_WAKER.wake(); - Self { phantom: PhantomData, alloc_in: Allocator::new(), alloc_out: Allocator::new(), + usb_supply, } } - /// Establish a new device that then uses the POWER peripheral to - /// detect USB power detected/removed events are handled. - #[cfg(not(feature = "_nrf5340-app"))] - pub fn with_power_management( - _usb: impl Unborrow + 'd, - irq: impl Unborrow + 'd, - power_irq: impl Interrupt, - ) -> Self { - let regs = unsafe { &*pac::POWER::ptr() }; - - power_irq.set_handler(Self::on_power_interrupt); - power_irq.unpend(); - power_irq.enable(); - - regs.intenset.write(|w| w.usbdetected().set().usbremoved().set()); - - Self::with_power_state(_usb, irq, regs.usbregstatus.read().vbusdetect().is_vbus_present()) - } - fn on_interrupt(_: *mut ()) { let regs = T::regs(); @@ -131,46 +231,13 @@ impl<'d, T: Instance> Driver<'d, T> { } } } - - #[cfg(not(feature = "_nrf5340-app"))] - fn on_power_interrupt(_: *mut ()) { - let regs = unsafe { &*pac::POWER::ptr() }; - - let mut power_changed = false; - let mut power_available = false; - - if regs.events_usbdetected.read().bits() != 0 { - regs.events_usbdetected.reset(); - power_changed = true; - power_available = true; - } - - if regs.events_usbremoved.read().bits() != 0 { - regs.events_usbremoved.reset(); - power_changed = true; - power_available = false; - } - - if power_changed { - POWER_AVAILABLE.store(power_available, Ordering::Relaxed); - BUS_WAKER.wake(); - } - } - - /// Manually declare that USB power is available or unavailable. - /// Useful in scenarios where power management cannot be managed - /// automatically e.g. when dealing with the nrf-softdevice. - pub fn power(available: bool) { - POWER_AVAILABLE.store(available, Ordering::Relaxed); - BUS_WAKER.wake(); - } } -impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { +impl<'d, T: Instance, P: UsbSupply + '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>; + type Bus = Bus<'d, T, P>; fn alloc_endpoint_in( &mut self, @@ -209,6 +276,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { Bus { phantom: PhantomData, power_available: false, + usb_supply: self.usb_supply, }, ControlPipe { _phantom: PhantomData, @@ -218,12 +286,13 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { } } -pub struct Bus<'d, T: Instance> { +pub struct Bus<'d, T: Instance, P: UsbSupply> { phantom: PhantomData<&'d mut T>, power_available: bool, + usb_supply: P, } -impl<'d, T: Instance> driver::Bus for Bus<'d, T> { +impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { type EnableFuture<'a> = impl Future + 'a where Self: 'a; type DisableFuture<'a> = impl Future + 'a where Self: 'a; type PollFuture<'a> = impl Future + 'a where Self: 'a; @@ -260,9 +329,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { w.epdata().set_bit(); w }); - // Enable the USB pullup, allowing enumeration. - regs.usbpullup.write(|w| w.connect().enabled()); - trace!("enabled"); + + 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"); + } } } @@ -318,7 +392,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { trace!("USB event: ready"); } - if POWER_AVAILABLE.load(Ordering::Relaxed) != self.power_available { + if self.usb_supply.is_usb_detected() != self.power_available { self.power_available = !self.power_available; if self.power_available { trace!("Power event: available"); diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index a20321fe8..e57cdaf63 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -15,14 +15,14 @@ use embassy::util::Forever; use embassy_net::tcp::TcpSocket; use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; use embassy_nrf::rng::Rng; -use embassy_nrf::usb::Driver; +use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals, Peripherals}; use embassy_usb::{Builder, Config, UsbDevice}; use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; -type MyDriver = Driver<'static, peripherals::USBD>; +type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; macro_rules! forever { ($val:expr) => {{ @@ -84,19 +84,15 @@ async fn net_task(stack: &'static Stack) -> ! { #[embassy::main] async fn main(spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; - let power: pac::POWER = unsafe { mem::transmute(()) }; info!("Enabling ext hfosc..."); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); while clock.events_hfclkstarted.read().bits() != 1 {} - info!("Waiting for vbus..."); - while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} - info!("vbus OK"); - // Create the driver, from the HAL. let irq = interrupt::take!(USBD); - let driver = Driver::new(p.USBD, irq); + let power_irq = interrupt::take!(POWER_CLOCK); + let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 97ec861d8..539ae6f16 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -10,10 +10,9 @@ use defmt::*; use embassy::channel::signal::Signal; use embassy::executor::Spawner; use embassy::time::Duration; -use embassy::util::{select, select3, Either, Either3}; +use embassy::util::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; -use embassy_nrf::interrupt::InterruptExt; -use embassy_nrf::usb::Driver; +use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, Peripherals}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; @@ -22,29 +21,11 @@ use futures::future::join; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; -static ENABLE_USB: Signal = Signal::new(); static SUSPENDED: AtomicBool = AtomicBool::new(false); -fn on_power_interrupt(_: *mut ()) { - let regs = unsafe { &*pac::POWER::ptr() }; - - if regs.events_usbdetected.read().bits() != 0 { - regs.events_usbdetected.reset(); - info!("Vbus detected, enabling USB..."); - ENABLE_USB.signal(true); - } - - if regs.events_usbremoved.read().bits() != 0 { - regs.events_usbremoved.reset(); - info!("Vbus removed, disabling USB..."); - ENABLE_USB.signal(false); - } -} - #[embassy::main] async fn main(_spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; - let power: pac::POWER = unsafe { mem::transmute(()) }; info!("Enabling ext hfosc..."); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); @@ -52,7 +33,8 @@ async fn main(_spawner: Spawner, p: Peripherals) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); - let driver = Driver::new(p.USBD, irq); + let power_irq = interrupt::take!(POWER_CLOCK); + let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -100,31 +82,11 @@ async fn main(_spawner: Spawner, p: Peripherals) { // Run the USB device. let usb_fut = async { - enable_command().await; loop { - match select(usb.run_until_suspend(), ENABLE_USB.wait()).await { - Either::First(_) => {} - Either::Second(enable) => { - if enable { - warn!("Enable when already enabled!"); - } else { - usb.disable().await; - enable_command().await; - } - } - } - - match select3(usb.wait_resume(), ENABLE_USB.wait(), remote_wakeup.wait()).await { - Either3::First(_) => (), - Either3::Second(enable) => { - if enable { - warn!("Enable when already enabled!"); - } else { - usb.disable().await; - enable_command().await; - } - } - Either3::Third(_) => unwrap!(usb.remote_wakeup().await), + usb.run_until_suspend().await; + match select(usb.wait_resume(), remote_wakeup.wait()).await { + Either::First(_) => (), + Either::Second(_) => unwrap!(usb.remote_wakeup().await), } } }; @@ -174,28 +136,11 @@ async fn main(_spawner: Spawner, p: Peripherals) { reader.run(false, &request_handler).await; }; - let power_irq = interrupt::take!(POWER_CLOCK); - power_irq.set_handler(on_power_interrupt); - power_irq.unpend(); - power_irq.enable(); - - power.intenset.write(|w| w.usbdetected().set().usbremoved().set()); - // Run everything concurrently. // If we had made everything `'static` above instead, we could do this using separate tasks instead. join(usb_fut, join(in_fut, out_fut)).await; } -async fn enable_command() { - loop { - if ENABLE_USB.wait().await { - break; - } else { - warn!("Received disable signal when already disabled!"); - } - } -} - struct MyRequestHandler {} impl RequestHandler for MyRequestHandler { diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index 9c44e5cc8..516e7ea95 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -8,7 +8,7 @@ use core::mem; use defmt::*; use embassy::executor::Spawner; use embassy::time::{Duration, Timer}; -use embassy_nrf::usb::Driver; +use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, Peripherals}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config}; @@ -20,19 +20,15 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy::main] async fn main(_spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; - let power: pac::POWER = unsafe { mem::transmute(()) }; info!("Enabling ext hfosc..."); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); while clock.events_hfclkstarted.read().bits() != 1 {} - info!("Waiting for vbus..."); - while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} - info!("vbus OK"); - // Create the driver, from the HAL. let irq = interrupt::take!(USBD); - let driver = Driver::new(p.USBD, irq); + let power_irq = interrupt::take!(POWER_CLOCK); + let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index 7c1d8cbb8..d2200dc5d 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -7,7 +7,7 @@ use core::mem; use defmt::{info, panic}; use embassy::executor::Spawner; -use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; use embassy_nrf::{interrupt, pac, Peripherals}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::with_power_management(p.USBD, irq, power_irq); + let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -97,7 +97,9 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { +async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( + class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, +) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { let n = class.read_packet(&mut buf).await?; diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs index dc503e67c..3806da5a0 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf/src/bin/usb_serial_multitask.rs @@ -8,14 +8,14 @@ use core::mem; use defmt::{info, panic, unwrap}; use embassy::executor::Spawner; use embassy::util::Forever; -use embassy_nrf::usb::Driver; +use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals, Peripherals}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config, UsbDevice}; use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; -type MyDriver = Driver<'static, peripherals::USBD>; +type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; #[embassy::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { @@ -35,19 +35,14 @@ async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) { #[embassy::main] async fn main(spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; - let power: pac::POWER = unsafe { mem::transmute(()) }; info!("Enabling ext hfosc..."); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); while clock.events_hfclkstarted.read().bits() != 1 {} - - info!("Waiting for vbus..."); - while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} - info!("vbus OK"); - // Create the driver, from the HAL. let irq = interrupt::take!(USBD); - let driver = Driver::new(p.USBD, irq); + let power_irq = interrupt::take!(POWER_CLOCK); + let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe);