From f5656e354485888cc7c2697052ae2c79b94a59ad Mon Sep 17 00:00:00 2001 From: alexmoon Date: Sun, 10 Apr 2022 15:41:51 -0400 Subject: Add DeviceStateHandler, DeviceCommand channel, and remote wakeup support --- examples/nrf/src/bin/usb_hid_keyboard.rs | 114 +++++++++++++++++++++++++-- examples/nrf/src/bin/usb_hid_mouse.rs | 3 +- examples/nrf/src/bin/usb_serial.rs | 3 +- examples/nrf/src/bin/usb_serial_multitask.rs | 3 +- 4 files changed, 113 insertions(+), 10 deletions(-) (limited to 'examples') diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 0812697e4..483d86b81 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -4,8 +4,12 @@ #![feature(type_alias_impl_trait)] use core::mem; +use core::sync::atomic::{AtomicBool, Ordering}; use defmt::*; +use embassy::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy::channel::Channel; use embassy::executor::Spawner; +use embassy::interrupt::InterruptExt; use embassy::time::Duration; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::interrupt; @@ -13,7 +17,7 @@ use embassy_nrf::pac; use embassy_nrf::usb::Driver; use embassy_nrf::Peripherals; use embassy_usb::control::OutResponse; -use embassy_usb::{Config, UsbDeviceBuilder}; +use embassy_usb::{Config, DeviceCommand, DeviceStateHandler, UsbDeviceBuilder}; use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State}; use futures::future::join; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; @@ -21,6 +25,29 @@ use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use defmt_rtt as _; // global logger use panic_probe as _; +static USB_COMMANDS: Channel = Channel::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..."); + if USB_COMMANDS.try_send(DeviceCommand::Enable).is_err() { + warn!("Failed to send enable command to USB channel"); + } + } + + if regs.events_usbremoved.read().bits() != 0 { + regs.events_usbremoved.reset(); + info!("Vbus removed, disabling USB..."); + if USB_COMMANDS.try_send(DeviceCommand::Disable).is_err() { + warn!("Failed to send disable command to USB channel"); + }; + } +} + #[embassy::main] async fn main(_spawner: Spawner, p: Peripherals) { let clock: pac::CLOCK = unsafe { mem::transmute(()) }; @@ -30,10 +57,6 @@ async fn main(_spawner: Spawner, p: Peripherals) { 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); @@ -45,6 +68,8 @@ async fn main(_spawner: Spawner, p: Peripherals) { config.serial_number = Some("12345678"); config.max_power = 100; config.max_packet_size_0 = 64; + config.supports_remote_wakeup = true; + config.start_enabled = false; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -53,16 +78,19 @@ async fn main(_spawner: Spawner, p: Peripherals) { let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 16]; let request_handler = MyRequestHandler {}; + let device_state_handler = MyDeviceStateHandler::new(); let mut state = State::<8, 1>::new(); - let mut builder = UsbDeviceBuilder::new( + let mut builder = UsbDeviceBuilder::new_with_channel( driver, config, &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut control_buf, + Some(&device_state_handler), + &USB_COMMANDS, ); // Create classes on the builder. @@ -76,7 +104,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { ); // Build the builder. - let mut usb = builder.build().await; + let mut usb = builder.build(); // Run the USB device. let usb_fut = usb.run(); @@ -90,6 +118,12 @@ async fn main(_spawner: Spawner, p: Peripherals) { loop { button.wait_for_low().await; info!("PRESSED"); + + if SUSPENDED.load(Ordering::Acquire) { + info!("Triggering remote wakeup"); + USB_COMMANDS.send(DeviceCommand::RemoteWakeup); + } + let report = KeyboardReport { keycodes: [4, 0, 0, 0, 0, 0], leds: 0, @@ -119,6 +153,16 @@ async fn main(_spawner: Spawner, p: Peripherals) { let out_fut = async { hid_out.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; @@ -146,3 +190,59 @@ impl RequestHandler for MyRequestHandler { None } } + +struct MyDeviceStateHandler { + configured: AtomicBool, +} + +impl MyDeviceStateHandler { + fn new() -> Self { + MyDeviceStateHandler { + configured: AtomicBool::new(false), + } + } +} + +impl DeviceStateHandler for MyDeviceStateHandler { + fn reset(&self) { + self.configured.store(false, Ordering::Relaxed); + info!("Bus reset, the Vbus current limit is 100mA"); + } + + fn addressed(&self, addr: u8) { + self.configured.store(false, Ordering::Relaxed); + info!("USB address set to: {}", addr); + } + + fn configured(&self, configured: bool) { + self.configured.store(configured, Ordering::Relaxed); + if configured { + info!( + "Device configured, it may now draw up to the configured current limit from Vbus." + ) + } else { + info!("Device is no longer configured, the Vbus current limit is 100mA."); + } + } + + fn suspended(&self, suspended: bool) { + if suspended { + info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); + SUSPENDED.store(true, Ordering::Release); + } else { + SUSPENDED.store(false, Ordering::Release); + if self.configured.load(Ordering::Relaxed) { + info!( + "Device resumed, it may now draw up to the configured current limit from Vbus" + ); + } else { + info!("Device resumed, the Vbus current limit is 100mA"); + } + } + } + + fn disabled(&self) { + self.configured.store(false, Ordering::Relaxed); + info!("Device disabled"); + } +} diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index ca9383827..fe27e76fb 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -61,6 +61,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, + None, ); // Create classes on the builder. @@ -74,7 +75,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { ); // Build the builder. - let mut usb = builder.build().await; + let mut usb = builder.build(); // Run the USB device. let usb_fut = usb.run(); diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index 684322837..987cc4139 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -54,13 +54,14 @@ async fn main(_spawner: Spawner, p: Peripherals) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, + None, ); // Create classes on the builder. let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); // Build the builder. - let mut usb = builder.build().await; + let mut usb = builder.build(); // Run the USB device. let usb_fut = usb.run(); diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs index bfb09014c..5fcb0e052 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf/src/bin/usb_serial_multitask.rs @@ -79,13 +79,14 @@ async fn main(spawner: Spawner, p: Peripherals) { &mut res.config_descriptor, &mut res.bos_descriptor, &mut res.control_buf, + None, ); // Create classes on the builder. let class = CdcAcmClass::new(&mut builder, &mut res.serial_state, 64); // Build the builder. - let usb = builder.build().await; + let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); unwrap!(spawner.spawn(echo_task(class))); -- cgit From 7fde3abd5d7e04f6d79d325d4454d6eafabebc1f Mon Sep 17 00:00:00 2001 From: alexmoon Date: Mon, 11 Apr 2022 12:00:05 -0400 Subject: Remote wakeup bug fixes --- examples/nrf/src/bin/usb_hid_keyboard.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'examples') diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 483d86b81..fb3a198a7 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -121,20 +121,20 @@ async fn main(_spawner: Spawner, p: Peripherals) { if SUSPENDED.load(Ordering::Acquire) { info!("Triggering remote wakeup"); - USB_COMMANDS.send(DeviceCommand::RemoteWakeup); + USB_COMMANDS.send(DeviceCommand::RemoteWakeup).await; + } else { + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match hid_in.serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; } - let report = KeyboardReport { - keycodes: [4, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - match hid_in.serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; - button.wait_for_high().await; info!("RELEASED"); let report = KeyboardReport { -- cgit From ff7c6b350e2338a0b1e4327f4b2eb0435468f313 Mon Sep 17 00:00:00 2001 From: alexmoon Date: Wed, 13 Apr 2022 13:09:08 -0400 Subject: Remove channel and make run future cancelable --- examples/nrf/src/bin/usb_hid_keyboard.rs | 63 +++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 14 deletions(-) (limited to 'examples') diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index fb3a198a7..32659dfbb 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -11,13 +11,14 @@ use embassy::channel::Channel; use embassy::executor::Spawner; use embassy::interrupt::InterruptExt; use embassy::time::Duration; +use embassy::util::select; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::interrupt; use embassy_nrf::pac; use embassy_nrf::usb::Driver; use embassy_nrf::Peripherals; use embassy_usb::control::OutResponse; -use embassy_usb::{Config, DeviceCommand, DeviceStateHandler, UsbDeviceBuilder}; +use embassy_usb::{Config, DeviceStateHandler, UsbDeviceBuilder}; use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State}; use futures::future::join; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; @@ -25,7 +26,14 @@ use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use defmt_rtt as _; // global logger use panic_probe as _; -static USB_COMMANDS: Channel = Channel::new(); +#[derive(defmt::Format)] +enum Command { + Enable, + Disable, + RemoteWakeup, +} + +static USB_COMMANDS: Channel = Channel::new(); static SUSPENDED: AtomicBool = AtomicBool::new(false); fn on_power_interrupt(_: *mut ()) { @@ -34,7 +42,7 @@ fn on_power_interrupt(_: *mut ()) { if regs.events_usbdetected.read().bits() != 0 { regs.events_usbdetected.reset(); info!("Vbus detected, enabling USB..."); - if USB_COMMANDS.try_send(DeviceCommand::Enable).is_err() { + if USB_COMMANDS.try_send(Command::Enable).is_err() { warn!("Failed to send enable command to USB channel"); } } @@ -42,7 +50,7 @@ fn on_power_interrupt(_: *mut ()) { if regs.events_usbremoved.read().bits() != 0 { regs.events_usbremoved.reset(); info!("Vbus removed, disabling USB..."); - if USB_COMMANDS.try_send(DeviceCommand::Disable).is_err() { + if USB_COMMANDS.try_send(Command::Disable).is_err() { warn!("Failed to send disable command to USB channel"); }; } @@ -69,7 +77,6 @@ async fn main(_spawner: Spawner, p: Peripherals) { config.max_power = 100; config.max_packet_size_0 = 64; config.supports_remote_wakeup = true; - config.start_enabled = false; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -82,7 +89,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { let mut state = State::<8, 1>::new(); - let mut builder = UsbDeviceBuilder::new_with_channel( + let mut builder = UsbDeviceBuilder::new( driver, config, &mut device_descriptor, @@ -90,7 +97,6 @@ async fn main(_spawner: Spawner, p: Peripherals) { &mut bos_descriptor, &mut control_buf, Some(&device_state_handler), - &USB_COMMANDS, ); // Create classes on the builder. @@ -107,7 +113,22 @@ async fn main(_spawner: Spawner, p: Peripherals) { let mut usb = builder.build(); // Run the USB device. - let usb_fut = usb.run(); + let usb_fut = async { + enable_command().await; + loop { + match select(usb.run(), USB_COMMANDS.recv()).await { + embassy::util::Either::First(_) => defmt::unreachable!(), + embassy::util::Either::Second(cmd) => match cmd { + Command::Enable => warn!("Enable when already enabled!"), + Command::Disable => { + usb.disable().await; + enable_command().await; + } + Command::RemoteWakeup => unwrap!(usb.remote_wakeup().await), + }, + } + } + }; let mut button = Input::new(p.P0_11.degrade(), Pull::Up); @@ -121,7 +142,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { if SUSPENDED.load(Ordering::Acquire) { info!("Triggering remote wakeup"); - USB_COMMANDS.send(DeviceCommand::RemoteWakeup).await; + USB_COMMANDS.send(Command::RemoteWakeup).await; } else { let report = KeyboardReport { keycodes: [4, 0, 0, 0, 0, 0], @@ -168,6 +189,15 @@ async fn main(_spawner: Spawner, p: Peripherals) { join(usb_fut, join(in_fut, out_fut)).await; } +async fn enable_command() { + loop { + match USB_COMMANDS.recv().await { + Command::Enable => break, + cmd => warn!("Received command {:?} when disabled!", cmd), + } + } +} + struct MyRequestHandler {} impl RequestHandler for MyRequestHandler { @@ -204,6 +234,16 @@ impl MyDeviceStateHandler { } impl DeviceStateHandler for MyDeviceStateHandler { + fn enabled(&self, enabled: bool) { + self.configured.store(false, Ordering::Relaxed); + SUSPENDED.store(false, Ordering::Release); + if enabled { + info!("Device enabled"); + } else { + info!("Device disabled"); + } + } + fn reset(&self) { self.configured.store(false, Ordering::Relaxed); info!("Bus reset, the Vbus current limit is 100mA"); @@ -240,9 +280,4 @@ impl DeviceStateHandler for MyDeviceStateHandler { } } } - - fn disabled(&self) { - self.configured.store(false, Ordering::Relaxed); - info!("Device disabled"); - } } -- cgit From b0725c14d36cb9c508cb9fcec5b92ca9e0148583 Mon Sep 17 00:00:00 2001 From: alexmoon Date: Wed, 13 Apr 2022 16:04:31 -0400 Subject: Split UsbDevice::run into run and run_until_suspend --- examples/nrf/src/bin/usb_hid_keyboard.rs | 60 +++++++++++++++++--------------- 1 file changed, 32 insertions(+), 28 deletions(-) (limited to 'examples') diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 32659dfbb..5f03f5126 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -6,12 +6,11 @@ use core::mem; use core::sync::atomic::{AtomicBool, Ordering}; use defmt::*; -use embassy::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy::channel::Channel; +use embassy::channel::Signal; use embassy::executor::Spawner; use embassy::interrupt::InterruptExt; use embassy::time::Duration; -use embassy::util::select; +use embassy::util::{select, select3, Either, Either3}; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::interrupt; use embassy_nrf::pac; @@ -26,14 +25,7 @@ use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use defmt_rtt as _; // global logger use panic_probe as _; -#[derive(defmt::Format)] -enum Command { - Enable, - Disable, - RemoteWakeup, -} - -static USB_COMMANDS: Channel = Channel::new(); +static ENABLE_USB: Signal = Signal::new(); static SUSPENDED: AtomicBool = AtomicBool::new(false); fn on_power_interrupt(_: *mut ()) { @@ -42,17 +34,13 @@ fn on_power_interrupt(_: *mut ()) { if regs.events_usbdetected.read().bits() != 0 { regs.events_usbdetected.reset(); info!("Vbus detected, enabling USB..."); - if USB_COMMANDS.try_send(Command::Enable).is_err() { - warn!("Failed to send enable command to USB channel"); - } + ENABLE_USB.signal(true); } if regs.events_usbremoved.read().bits() != 0 { regs.events_usbremoved.reset(); info!("Vbus removed, disabling USB..."); - if USB_COMMANDS.try_send(Command::Disable).is_err() { - warn!("Failed to send disable command to USB channel"); - }; + ENABLE_USB.signal(false); } } @@ -112,20 +100,35 @@ async fn main(_spawner: Spawner, p: Peripherals) { // Build the builder. let mut usb = builder.build(); + let remote_wakeup = Signal::new(); + // Run the USB device. let usb_fut = async { enable_command().await; loop { - match select(usb.run(), USB_COMMANDS.recv()).await { - embassy::util::Either::First(_) => defmt::unreachable!(), - embassy::util::Either::Second(cmd) => match cmd { - Command::Enable => warn!("Enable when already enabled!"), - Command::Disable => { + 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; } - Command::RemoteWakeup => unwrap!(usb.remote_wakeup().await), - }, + } + Either3::Third(_) => unwrap!(usb.remote_wakeup().await), } } }; @@ -142,7 +145,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { if SUSPENDED.load(Ordering::Acquire) { info!("Triggering remote wakeup"); - USB_COMMANDS.send(Command::RemoteWakeup).await; + remote_wakeup.signal(()); } else { let report = KeyboardReport { keycodes: [4, 0, 0, 0, 0, 0], @@ -191,9 +194,10 @@ async fn main(_spawner: Spawner, p: Peripherals) { async fn enable_command() { loop { - match USB_COMMANDS.recv().await { - Command::Enable => break, - cmd => warn!("Received command {:?} when disabled!", cmd), + if ENABLE_USB.wait().await { + break; + } else { + warn!("Received disable signal when already disabled!"); } } } -- cgit