aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-usb/src/lib.rs1
-rw-r--r--embassy-usb/src/util.rs68
-rw-r--r--examples/nrf/src/bin/usb_serial.rs38
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;
11mod descriptor_reader; 11mod descriptor_reader;
12pub mod driver; 12pub mod driver;
13pub mod types; 13pub mod types;
14pub mod util;
14 15
15use embassy::util::{select, Either}; 16use embassy::util::{select, Either};
16use heapless::Vec; 17use 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 @@
1use embassy::channel::signal::Signal;
2use embassy::util::{select, Either};
3
4use crate::driver::Driver;
5use 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.
17pub struct EnabledUsbDevice<'d, D: Driver<'d>> {
18 pub underlying: UsbDevice<'d, D>,
19 enable_usb_signal: &'d Signal<bool>,
20}
21
22impl<'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 @@
6use core::mem; 6use core::mem;
7 7
8use defmt::{info, panic}; 8use defmt::{info, panic};
9use embassy::channel::signal::Signal;
9use embassy::executor::Spawner; 10use embassy::executor::Spawner;
11use embassy::interrupt::InterruptExt;
10use embassy_nrf::usb::{Driver, Instance}; 12use embassy_nrf::usb::{Driver, Instance};
11use embassy_nrf::{interrupt, pac, Peripherals}; 13use embassy_nrf::{interrupt, interrupt, pac, pac, Peripherals};
12use embassy_usb::driver::EndpointError; 14use embassy_usb::driver::EndpointError;
15use embassy_usb::util::EnabledUsbDevice;
13use embassy_usb::{Builder, Config}; 16use embassy_usb::{Builder, Config};
14use embassy_usb_serial::{CdcAcmClass, State}; 17use embassy_usb_serial::{CdcAcmClass, State};
15use futures::future::join; 18use futures::future::join;
16use {defmt_rtt as _, panic_probe as _}; 19use {defmt_rtt as _, panic_probe as _}; // global logger
20
21static ENABLE_USB: Signal<bool> = Signal::new();
22
23fn 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]
19async fn main(_spawner: Spawner, p: Peripherals) { 40async 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();