diff options
| -rw-r--r-- | embassy-usb/src/lib.rs | 1 | ||||
| -rw-r--r-- | embassy-usb/src/util.rs | 68 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb_serial.rs | 38 |
3 files changed, 100 insertions, 7 deletions
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; | |||
| 11 | mod descriptor_reader; | 11 | mod descriptor_reader; |
| 12 | pub mod driver; | 12 | pub mod driver; |
| 13 | pub mod types; | 13 | pub mod types; |
| 14 | pub mod util; | ||
| 14 | 15 | ||
| 15 | use embassy::util::{select, Either}; | 16 | use embassy::util::{select, Either}; |
| 16 | use heapless::Vec; | 17 | 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 @@ | |||
| 1 | use embassy::channel::signal::Signal; | ||
| 2 | use embassy::util::{select, Either}; | ||
| 3 | |||
| 4 | use crate::driver::Driver; | ||
| 5 | use crate::UsbDevice; | ||
| 6 | |||
| 7 | /// Am enabled usb device is a device that further receives external notifications | ||
| 8 | /// regarding whether it is enabled or not. A common example of where this is | ||
| 9 | /// required is when receiving notifications from the POWER peripheral that | ||
| 10 | /// USB has been connected to or removed. The device here wraps an existing | ||
| 11 | /// USB device, keeping it publically available so that device-oriented operations | ||
| 12 | /// may still be performed. A signal is also provided that enables/disables the | ||
| 13 | /// USB device, taking care of suspension and resumption. In the case of the POWER | ||
| 14 | /// peripheral, this signal can be used from within a POWER_CLOCK interrupt | ||
| 15 | /// handler. Alternatively, for softdevice usage where the POWER peripheral is not | ||
| 16 | /// available, similar USB power events can be leveraged. | ||
| 17 | pub struct EnabledUsbDevice<'d, D: Driver<'d>> { | ||
| 18 | pub underlying: UsbDevice<'d, D>, | ||
| 19 | enable_usb_signal: &'d Signal<bool>, | ||
| 20 | } | ||
| 21 | |||
| 22 | impl<'d, D: Driver<'d>> EnabledUsbDevice<'d, D> { | ||
| 23 | /// Wrap an existing UsbDevice and take a signal that will be used | ||
| 24 | /// to enable/disable it, perhaps from an external POWER_CLOCK | ||
| 25 | /// interrupt, or the equivalent when dealing with softdevices. | ||
| 26 | pub fn new(underlying: UsbDevice<'d, D>, enable_usb_signal: &'d Signal<bool>) -> Self { | ||
| 27 | Self { | ||
| 28 | underlying, | ||
| 29 | enable_usb_signal, | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Runs the underlying `UsbDevice` taking care of reacting to USB becoming | ||
| 34 | /// enabled/disabled. | ||
| 35 | /// | ||
| 36 | /// This future may leave the bus in an invalid state if it is dropped. | ||
| 37 | /// After dropping the future, [`UsbDevice::disable()`] should be called | ||
| 38 | /// before calling any other `UsbDevice` methods to fully reset the | ||
| 39 | /// peripheral. | ||
| 40 | pub async fn run(&mut self) -> ! { | ||
| 41 | while !self.enable_usb_signal.wait().await {} | ||
| 42 | loop { | ||
| 43 | match select( | ||
| 44 | self.underlying.run_until_suspend(), | ||
| 45 | self.enable_usb_signal.wait(), | ||
| 46 | ) | ||
| 47 | .await | ||
| 48 | { | ||
| 49 | Either::First(_) => {} | ||
| 50 | Either::Second(enable) => { | ||
| 51 | if !enable { | ||
| 52 | self.underlying.disable().await; | ||
| 53 | while !self.enable_usb_signal.wait().await {} | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | match select(self.underlying.wait_resume(), self.enable_usb_signal.wait()).await { | ||
| 58 | Either::First(_) => {} | ||
| 59 | Either::Second(enable) => { | ||
| 60 | if !enable { | ||
| 61 | self.underlying.disable().await; | ||
| 62 | while !self.enable_usb_signal.wait().await {} | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
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 @@ | |||
| 6 | use core::mem; | 6 | use core::mem; |
| 7 | 7 | ||
| 8 | use defmt::{info, panic}; | 8 | use defmt::{info, panic}; |
| 9 | use embassy::channel::signal::Signal; | ||
| 9 | use embassy::executor::Spawner; | 10 | use embassy::executor::Spawner; |
| 11 | use embassy::interrupt::InterruptExt; | ||
| 10 | use embassy_nrf::usb::{Driver, Instance}; | 12 | use embassy_nrf::usb::{Driver, Instance}; |
| 11 | use embassy_nrf::{interrupt, pac, Peripherals}; | 13 | use embassy_nrf::{interrupt, interrupt, pac, pac, Peripherals}; |
| 12 | use embassy_usb::driver::EndpointError; | 14 | use embassy_usb::driver::EndpointError; |
| 15 | use embassy_usb::util::EnabledUsbDevice; | ||
| 13 | use embassy_usb::{Builder, Config}; | 16 | use embassy_usb::{Builder, Config}; |
| 14 | use embassy_usb_serial::{CdcAcmClass, State}; | 17 | use embassy_usb_serial::{CdcAcmClass, State}; |
| 15 | use futures::future::join; | 18 | use futures::future::join; |
| 16 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {defmt_rtt as _, panic_probe as _}; // global logger |
| 20 | |||
| 21 | static ENABLE_USB: Signal<bool> = Signal::new(); | ||
| 22 | |||
| 23 | fn on_power_interrupt(_: *mut ()) { | ||
| 24 | let regs = unsafe { &*pac::POWER::ptr() }; | ||
| 25 | |||
| 26 | if regs.events_usbdetected.read().bits() != 0 { | ||
| 27 | regs.events_usbdetected.reset(); | ||
| 28 | info!("Vbus detected, enabling USB..."); | ||
| 29 | ENABLE_USB.signal(true); | ||
| 30 | } | ||
| 31 | |||
| 32 | if regs.events_usbremoved.read().bits() != 0 { | ||
| 33 | regs.events_usbremoved.reset(); | ||
| 34 | info!("Vbus removed, disabling USB..."); | ||
| 35 | ENABLE_USB.signal(false); | ||
| 36 | } | ||
| 37 | } | ||
| 17 | 38 | ||
| 18 | #[embassy::main] | 39 | #[embassy::main] |
| 19 | async fn main(_spawner: Spawner, p: Peripherals) { | 40 | async fn main(_spawner: Spawner, p: Peripherals) { |
| 20 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | 41 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; |
| 21 | let power: pac::POWER = unsafe { mem::transmute(()) }; | 42 | let power: pac::POWER = unsafe { mem::transmute(()) }; |
| 22 | 43 | ||
| 44 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 45 | power_irq.set_handler(on_power_interrupt); | ||
| 46 | power_irq.unpend(); | ||
| 47 | power_irq.enable(); | ||
| 48 | |||
| 49 | power.intenset.write(|w| w.usbdetected().set().usbremoved().set()); | ||
| 50 | |||
| 23 | info!("Enabling ext hfosc..."); | 51 | info!("Enabling ext hfosc..."); |
| 24 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | 52 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); |
| 25 | while clock.events_hfclkstarted.read().bits() != 1 {} | 53 | while clock.events_hfclkstarted.read().bits() != 1 {} |
| 26 | 54 | ||
| 27 | info!("Waiting for vbus..."); | ||
| 28 | while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} | ||
| 29 | info!("vbus OK"); | ||
| 30 | |||
| 31 | // Create the driver, from the HAL. | 55 | // Create the driver, from the HAL. |
| 32 | let irq = interrupt::take!(USBD); | 56 | let irq = interrupt::take!(USBD); |
| 33 | let driver = Driver::new(p.USBD, irq); | 57 | let driver = Driver::new(p.USBD, irq); |
| @@ -70,7 +94,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 70 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | 94 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); |
| 71 | 95 | ||
| 72 | // Build the builder. | 96 | // Build the builder. |
| 73 | let mut usb = builder.build(); | 97 | let mut usb = EnabledUsbDevice::new(builder.build(), &ENABLE_USB); |
| 74 | 98 | ||
| 75 | // Run the USB device. | 99 | // Run the USB device. |
| 76 | let usb_fut = usb.run(); | 100 | let usb_fut = usb.run(); |
