diff options
| -rw-r--r-- | embassy-usb/src/lib.rs | 129 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb_hid_keyboard.rs | 60 |
2 files changed, 113 insertions, 76 deletions
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index ccea8bc7a..baeca099f 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs | |||
| @@ -100,7 +100,6 @@ pub struct UsbDevice<'d, D: Driver<'d>> { | |||
| 100 | 100 | ||
| 101 | device_state: UsbDeviceState, | 101 | device_state: UsbDeviceState, |
| 102 | suspended: bool, | 102 | suspended: bool, |
| 103 | in_control_handler: bool, | ||
| 104 | remote_wakeup_enabled: bool, | 103 | remote_wakeup_enabled: bool, |
| 105 | self_powered: bool, | 104 | self_powered: bool, |
| 106 | pending_address: u8, | 105 | pending_address: u8, |
| @@ -138,7 +137,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 138 | control_buf, | 137 | control_buf, |
| 139 | device_state: UsbDeviceState::Disabled, | 138 | device_state: UsbDeviceState::Disabled, |
| 140 | suspended: false, | 139 | suspended: false, |
| 141 | in_control_handler: false, | ||
| 142 | remote_wakeup_enabled: false, | 140 | remote_wakeup_enabled: false, |
| 143 | self_powered: false, | 141 | self_powered: false, |
| 144 | pending_address: 0, | 142 | pending_address: 0, |
| @@ -146,7 +144,26 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 146 | } | 144 | } |
| 147 | } | 145 | } |
| 148 | 146 | ||
| 147 | /// Runs the `UsbDevice` forever. | ||
| 148 | /// | ||
| 149 | /// This future may leave the bus in an invalid state if it is dropped. | ||
| 150 | /// After dropping the future, [`UsbDevice::disable()`] should be called | ||
| 151 | /// before calling any other `UsbDevice` methods to fully reset the | ||
| 152 | /// peripheral. | ||
| 149 | pub async fn run(&mut self) -> ! { | 153 | pub async fn run(&mut self) -> ! { |
| 154 | loop { | ||
| 155 | self.run_until_suspend().await; | ||
| 156 | self.wait_resume().await; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | /// Runs the `UsbDevice` until the bus is suspended. | ||
| 161 | /// | ||
| 162 | /// This future may leave the bus in an invalid state if it is dropped. | ||
| 163 | /// After dropping the future, [`UsbDevice::disable()`] should be called | ||
| 164 | /// before calling any other `UsbDevice` methods to fully reset the | ||
| 165 | /// peripheral. | ||
| 166 | pub async fn run_until_suspend(&mut self) -> () { | ||
| 150 | if self.device_state == UsbDeviceState::Disabled { | 167 | if self.device_state == UsbDeviceState::Disabled { |
| 151 | self.bus.enable().await; | 168 | self.bus.enable().await; |
| 152 | self.device_state = UsbDeviceState::Default; | 169 | self.device_state = UsbDeviceState::Default; |
| @@ -154,66 +171,33 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 154 | if let Some(h) = &self.handler { | 171 | if let Some(h) = &self.handler { |
| 155 | h.enabled(true); | 172 | h.enabled(true); |
| 156 | } | 173 | } |
| 157 | } else if self.in_control_handler { | ||
| 158 | warn!("usb: control request interrupted"); | ||
| 159 | self.control.reject(); | ||
| 160 | self.in_control_handler = false; | ||
| 161 | } | 174 | } |
| 162 | 175 | ||
| 163 | loop { | 176 | loop { |
| 164 | let control_fut = self.control.setup(); | 177 | let control_fut = self.control.setup(); |
| 165 | let bus_fut = self.bus.poll(); | 178 | let bus_fut = self.bus.poll(); |
| 166 | match select(bus_fut, control_fut).await { | 179 | match select(bus_fut, control_fut).await { |
| 167 | Either::First(evt) => match evt { | 180 | Either::First(evt) => { |
| 168 | Event::Reset => { | 181 | self.handle_bus_event(evt); |
| 169 | trace!("usb: reset"); | 182 | if self.suspended { |
| 170 | self.device_state = UsbDeviceState::Default; | 183 | return; |
| 171 | self.suspended = false; | ||
| 172 | self.remote_wakeup_enabled = false; | ||
| 173 | self.pending_address = 0; | ||
| 174 | |||
| 175 | for (_, h) in self.interfaces.iter_mut() { | ||
| 176 | h.reset(); | ||
| 177 | } | ||
| 178 | |||
| 179 | if let Some(h) = &self.handler { | ||
| 180 | h.reset(); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | Event::Resume => { | ||
| 184 | trace!("usb: resume"); | ||
| 185 | self.suspended = false; | ||
| 186 | if let Some(h) = &self.handler { | ||
| 187 | h.suspended(false); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | Event::Suspend => { | ||
| 191 | trace!("usb: suspend"); | ||
| 192 | self.suspended = true; | ||
| 193 | if let Some(h) = &self.handler { | ||
| 194 | h.suspended(true); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | }, | ||
| 198 | Either::Second(req) => { | ||
| 199 | self.in_control_handler = true; | ||
| 200 | match req { | ||
| 201 | Setup::DataIn(req, stage) => self.handle_control_in(req, stage).await, | ||
| 202 | Setup::DataOut(req, stage) => self.handle_control_out(req, stage).await, | ||
| 203 | } | 184 | } |
| 204 | self.in_control_handler = false; | ||
| 205 | } | 185 | } |
| 186 | Either::Second(req) => match req { | ||
| 187 | Setup::DataIn(req, stage) => self.handle_control_in(req, stage).await, | ||
| 188 | Setup::DataOut(req, stage) => self.handle_control_out(req, stage).await, | ||
| 189 | }, | ||
| 206 | } | 190 | } |
| 207 | } | 191 | } |
| 208 | } | 192 | } |
| 209 | 193 | ||
| 194 | /// Disables the USB peripheral. | ||
| 210 | pub async fn disable(&mut self) { | 195 | pub async fn disable(&mut self) { |
| 211 | if self.device_state != UsbDeviceState::Disabled { | 196 | if self.device_state != UsbDeviceState::Disabled { |
| 212 | self.bus.disable().await; | 197 | self.bus.disable().await; |
| 213 | self.device_state = UsbDeviceState::Disabled; | 198 | self.device_state = UsbDeviceState::Disabled; |
| 214 | self.suspended = false; | 199 | self.suspended = false; |
| 215 | self.remote_wakeup_enabled = false; | 200 | self.remote_wakeup_enabled = false; |
| 216 | self.in_control_handler = false; | ||
| 217 | 201 | ||
| 218 | if let Some(h) = &self.handler { | 202 | if let Some(h) = &self.handler { |
| 219 | h.enabled(false); | 203 | h.enabled(false); |
| @@ -221,11 +205,26 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 221 | } | 205 | } |
| 222 | } | 206 | } |
| 223 | 207 | ||
| 208 | /// Waits for a resume condition on the USB bus. | ||
| 209 | /// | ||
| 210 | /// This future is cancel-safe. | ||
| 211 | pub async fn wait_resume(&mut self) { | ||
| 212 | while self.suspended { | ||
| 213 | let evt = self.bus.poll().await; | ||
| 214 | self.handle_bus_event(evt); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | /// Initiates a device remote wakeup on the USB bus. | ||
| 219 | /// | ||
| 220 | /// If the bus is not suspended or remote wakeup is not enabled, an error | ||
| 221 | /// will be returned. | ||
| 222 | /// | ||
| 223 | /// This future may leave the bus in an inconsistent state if dropped. | ||
| 224 | /// After dropping the future, [`UsbDevice::disable()`] should be called | ||
| 225 | /// before calling any other `UsbDevice` methods to fully reset the peripheral. | ||
| 224 | pub async fn remote_wakeup(&mut self) -> Result<(), RemoteWakeupError> { | 226 | pub async fn remote_wakeup(&mut self) -> Result<(), RemoteWakeupError> { |
| 225 | if self.device_state == UsbDeviceState::Configured | 227 | if self.suspended && self.remote_wakeup_enabled { |
| 226 | && self.suspended | ||
| 227 | && self.remote_wakeup_enabled | ||
| 228 | { | ||
| 229 | self.bus.remote_wakeup().await?; | 228 | self.bus.remote_wakeup().await?; |
| 230 | self.suspended = false; | 229 | self.suspended = false; |
| 231 | 230 | ||
| @@ -239,6 +238,40 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 239 | } | 238 | } |
| 240 | } | 239 | } |
| 241 | 240 | ||
| 241 | fn handle_bus_event(&mut self, evt: Event) { | ||
| 242 | match evt { | ||
| 243 | Event::Reset => { | ||
| 244 | trace!("usb: reset"); | ||
| 245 | self.device_state = UsbDeviceState::Default; | ||
| 246 | self.suspended = false; | ||
| 247 | self.remote_wakeup_enabled = false; | ||
| 248 | self.pending_address = 0; | ||
| 249 | |||
| 250 | for (_, h) in self.interfaces.iter_mut() { | ||
| 251 | h.reset(); | ||
| 252 | } | ||
| 253 | |||
| 254 | if let Some(h) = &self.handler { | ||
| 255 | h.reset(); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | Event::Resume => { | ||
| 259 | trace!("usb: resume"); | ||
| 260 | self.suspended = false; | ||
| 261 | if let Some(h) = &self.handler { | ||
| 262 | h.suspended(false); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | Event::Suspend => { | ||
| 266 | trace!("usb: suspend"); | ||
| 267 | self.suspended = true; | ||
| 268 | if let Some(h) = &self.handler { | ||
| 269 | h.suspended(true); | ||
| 270 | } | ||
| 271 | } | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 242 | async fn handle_control_out(&mut self, req: Request, stage: DataOutStage) { | 275 | async fn handle_control_out(&mut self, req: Request, stage: DataOutStage) { |
| 243 | const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16; | 276 | const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16; |
| 244 | const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16; | 277 | const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16; |
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 @@ | |||
| 6 | use core::mem; | 6 | use core::mem; |
| 7 | use core::sync::atomic::{AtomicBool, Ordering}; | 7 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 8 | use defmt::*; | 8 | use defmt::*; |
| 9 | use embassy::blocking_mutex::raw::CriticalSectionRawMutex; | 9 | use embassy::channel::Signal; |
| 10 | use embassy::channel::Channel; | ||
| 11 | use embassy::executor::Spawner; | 10 | use embassy::executor::Spawner; |
| 12 | use embassy::interrupt::InterruptExt; | 11 | use embassy::interrupt::InterruptExt; |
| 13 | use embassy::time::Duration; | 12 | use embassy::time::Duration; |
| 14 | use embassy::util::select; | 13 | use embassy::util::{select, select3, Either, Either3}; |
| 15 | use embassy_nrf::gpio::{Input, Pin, Pull}; | 14 | use embassy_nrf::gpio::{Input, Pin, Pull}; |
| 16 | use embassy_nrf::interrupt; | 15 | use embassy_nrf::interrupt; |
| 17 | use embassy_nrf::pac; | 16 | use embassy_nrf::pac; |
| @@ -26,14 +25,7 @@ use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | |||
| 26 | use defmt_rtt as _; // global logger | 25 | use defmt_rtt as _; // global logger |
| 27 | use panic_probe as _; | 26 | use panic_probe as _; |
| 28 | 27 | ||
| 29 | #[derive(defmt::Format)] | 28 | static ENABLE_USB: Signal<bool> = Signal::new(); |
| 30 | enum Command { | ||
| 31 | Enable, | ||
| 32 | Disable, | ||
| 33 | RemoteWakeup, | ||
| 34 | } | ||
| 35 | |||
| 36 | static USB_COMMANDS: Channel<CriticalSectionRawMutex, Command, 1> = Channel::new(); | ||
| 37 | static SUSPENDED: AtomicBool = AtomicBool::new(false); | 29 | static SUSPENDED: AtomicBool = AtomicBool::new(false); |
| 38 | 30 | ||
| 39 | fn on_power_interrupt(_: *mut ()) { | 31 | fn on_power_interrupt(_: *mut ()) { |
| @@ -42,17 +34,13 @@ fn on_power_interrupt(_: *mut ()) { | |||
| 42 | if regs.events_usbdetected.read().bits() != 0 { | 34 | if regs.events_usbdetected.read().bits() != 0 { |
| 43 | regs.events_usbdetected.reset(); | 35 | regs.events_usbdetected.reset(); |
| 44 | info!("Vbus detected, enabling USB..."); | 36 | info!("Vbus detected, enabling USB..."); |
| 45 | if USB_COMMANDS.try_send(Command::Enable).is_err() { | 37 | ENABLE_USB.signal(true); |
| 46 | warn!("Failed to send enable command to USB channel"); | ||
| 47 | } | ||
| 48 | } | 38 | } |
| 49 | 39 | ||
| 50 | if regs.events_usbremoved.read().bits() != 0 { | 40 | if regs.events_usbremoved.read().bits() != 0 { |
| 51 | regs.events_usbremoved.reset(); | 41 | regs.events_usbremoved.reset(); |
| 52 | info!("Vbus removed, disabling USB..."); | 42 | info!("Vbus removed, disabling USB..."); |
| 53 | if USB_COMMANDS.try_send(Command::Disable).is_err() { | 43 | ENABLE_USB.signal(false); |
| 54 | warn!("Failed to send disable command to USB channel"); | ||
| 55 | }; | ||
| 56 | } | 44 | } |
| 57 | } | 45 | } |
| 58 | 46 | ||
| @@ -112,20 +100,35 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 112 | // Build the builder. | 100 | // Build the builder. |
| 113 | let mut usb = builder.build(); | 101 | let mut usb = builder.build(); |
| 114 | 102 | ||
| 103 | let remote_wakeup = Signal::new(); | ||
| 104 | |||
| 115 | // Run the USB device. | 105 | // Run the USB device. |
| 116 | let usb_fut = async { | 106 | let usb_fut = async { |
| 117 | enable_command().await; | 107 | enable_command().await; |
| 118 | loop { | 108 | loop { |
| 119 | match select(usb.run(), USB_COMMANDS.recv()).await { | 109 | match select(usb.run_until_suspend(), ENABLE_USB.wait()).await { |
| 120 | embassy::util::Either::First(_) => defmt::unreachable!(), | 110 | Either::First(_) => {} |
| 121 | embassy::util::Either::Second(cmd) => match cmd { | 111 | Either::Second(enable) => { |
| 122 | Command::Enable => warn!("Enable when already enabled!"), | 112 | if enable { |
| 123 | Command::Disable => { | 113 | warn!("Enable when already enabled!"); |
| 114 | } else { | ||
| 115 | usb.disable().await; | ||
| 116 | enable_command().await; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | match select3(usb.wait_resume(), ENABLE_USB.wait(), remote_wakeup.wait()).await { | ||
| 122 | Either3::First(_) => (), | ||
| 123 | Either3::Second(enable) => { | ||
| 124 | if enable { | ||
| 125 | warn!("Enable when already enabled!"); | ||
| 126 | } else { | ||
| 124 | usb.disable().await; | 127 | usb.disable().await; |
| 125 | enable_command().await; | 128 | enable_command().await; |
| 126 | } | 129 | } |
| 127 | Command::RemoteWakeup => unwrap!(usb.remote_wakeup().await), | 130 | } |
| 128 | }, | 131 | Either3::Third(_) => unwrap!(usb.remote_wakeup().await), |
| 129 | } | 132 | } |
| 130 | } | 133 | } |
| 131 | }; | 134 | }; |
| @@ -142,7 +145,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 142 | 145 | ||
| 143 | if SUSPENDED.load(Ordering::Acquire) { | 146 | if SUSPENDED.load(Ordering::Acquire) { |
| 144 | info!("Triggering remote wakeup"); | 147 | info!("Triggering remote wakeup"); |
| 145 | USB_COMMANDS.send(Command::RemoteWakeup).await; | 148 | remote_wakeup.signal(()); |
| 146 | } else { | 149 | } else { |
| 147 | let report = KeyboardReport { | 150 | let report = KeyboardReport { |
| 148 | keycodes: [4, 0, 0, 0, 0, 0], | 151 | keycodes: [4, 0, 0, 0, 0, 0], |
| @@ -191,9 +194,10 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 191 | 194 | ||
| 192 | async fn enable_command() { | 195 | async fn enable_command() { |
| 193 | loop { | 196 | loop { |
| 194 | match USB_COMMANDS.recv().await { | 197 | if ENABLE_USB.wait().await { |
| 195 | Command::Enable => break, | 198 | break; |
| 196 | cmd => warn!("Received command {:?} when disabled!", cmd), | 199 | } else { |
| 200 | warn!("Received disable signal when already disabled!"); | ||
| 197 | } | 201 | } |
| 198 | } | 202 | } |
| 199 | } | 203 | } |
