diff options
| author | Matt Spencer <[email protected]> | 2023-09-19 15:42:26 +0100 |
|---|---|---|
| committer | Matt Spencer <[email protected]> | 2023-09-19 15:42:26 +0100 |
| commit | a402aed3d1738a2afee4b78d01344cba636f6a1d (patch) | |
| tree | e9e99525b1055169d244ddf78eca7f9c50ec832d /embassy-usb/src | |
| parent | 183824fbddb2a05aed4ed854f6eb08fe4ba7e7c1 (diff) | |
Add async interface for CDC control changes
Signed-off-by: Matt Spencer <[email protected]>
Diffstat (limited to 'embassy-usb/src')
| -rw-r--r-- | embassy-usb/src/class/cdc_acm.rs | 70 |
1 files changed, 68 insertions, 2 deletions
diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index a341e10da..0c708464d 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs | |||
| @@ -1,10 +1,13 @@ | |||
| 1 | //! CDC-ACM class implementation, aka Serial over USB. | 1 | //! CDC-ACM class implementation, aka Serial over USB. |
| 2 | 2 | ||
| 3 | use core::cell::Cell; | 3 | use core::cell::{Cell, RefCell}; |
| 4 | use core::future::poll_fn; | ||
| 4 | use core::mem::{self, MaybeUninit}; | 5 | use core::mem::{self, MaybeUninit}; |
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | 6 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 7 | use core::task::Poll; | ||
| 6 | 8 | ||
| 7 | use embassy_sync::blocking_mutex::CriticalSectionMutex; | 9 | use embassy_sync::blocking_mutex::CriticalSectionMutex; |
| 10 | use embassy_sync::waitqueue::WakerRegistration; | ||
| 8 | 11 | ||
| 9 | use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; | 12 | use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; |
| 10 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; | 13 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; |
| @@ -76,6 +79,9 @@ struct ControlShared { | |||
| 76 | line_coding: CriticalSectionMutex<Cell<LineCoding>>, | 79 | line_coding: CriticalSectionMutex<Cell<LineCoding>>, |
| 77 | dtr: AtomicBool, | 80 | dtr: AtomicBool, |
| 78 | rts: AtomicBool, | 81 | rts: AtomicBool, |
| 82 | |||
| 83 | waker: RefCell<WakerRegistration>, | ||
| 84 | changed: AtomicBool, | ||
| 79 | } | 85 | } |
| 80 | 86 | ||
| 81 | impl Default for ControlShared { | 87 | impl Default for ControlShared { |
| @@ -89,10 +95,28 @@ impl Default for ControlShared { | |||
| 89 | parity_type: ParityType::None, | 95 | parity_type: ParityType::None, |
| 90 | data_rate: 8_000, | 96 | data_rate: 8_000, |
| 91 | })), | 97 | })), |
| 98 | waker: RefCell::new(WakerRegistration::new()), | ||
| 99 | changed: AtomicBool::new(false), | ||
| 92 | } | 100 | } |
| 93 | } | 101 | } |
| 94 | } | 102 | } |
| 95 | 103 | ||
| 104 | impl ControlShared { | ||
| 105 | async fn changed(&self) { | ||
| 106 | poll_fn(|cx| match self.changed.load(Ordering::Relaxed) { | ||
| 107 | true => { | ||
| 108 | self.changed.store(false, Ordering::Relaxed); | ||
| 109 | Poll::Ready(()) | ||
| 110 | } | ||
| 111 | false => { | ||
| 112 | self.waker.borrow_mut().register(cx.waker()); | ||
| 113 | Poll::Pending | ||
| 114 | } | ||
| 115 | }) | ||
| 116 | .await | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 96 | impl<'a> Control<'a> { | 120 | impl<'a> Control<'a> { |
| 97 | fn shared(&mut self) -> &'a ControlShared { | 121 | fn shared(&mut self) -> &'a ControlShared { |
| 98 | self.shared | 122 | self.shared |
| @@ -105,6 +129,9 @@ impl<'d> Handler for Control<'d> { | |||
| 105 | shared.line_coding.lock(|x| x.set(LineCoding::default())); | 129 | shared.line_coding.lock(|x| x.set(LineCoding::default())); |
| 106 | shared.dtr.store(false, Ordering::Relaxed); | 130 | shared.dtr.store(false, Ordering::Relaxed); |
| 107 | shared.rts.store(false, Ordering::Relaxed); | 131 | shared.rts.store(false, Ordering::Relaxed); |
| 132 | |||
| 133 | shared.changed.store(true, Ordering::Relaxed); | ||
| 134 | shared.waker.borrow_mut().wake(); | ||
| 108 | } | 135 | } |
| 109 | 136 | ||
| 110 | fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> { | 137 | fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> { |
| @@ -127,9 +154,13 @@ impl<'d> Handler for Control<'d> { | |||
| 127 | parity_type: data[5].into(), | 154 | parity_type: data[5].into(), |
| 128 | data_bits: data[6], | 155 | data_bits: data[6], |
| 129 | }; | 156 | }; |
| 130 | self.shared().line_coding.lock(|x| x.set(coding)); | 157 | let shared = self.shared(); |
| 158 | shared.line_coding.lock(|x| x.set(coding)); | ||
| 131 | debug!("Set line coding to: {:?}", coding); | 159 | debug!("Set line coding to: {:?}", coding); |
| 132 | 160 | ||
| 161 | shared.changed.store(true, Ordering::Relaxed); | ||
| 162 | shared.waker.borrow_mut().wake(); | ||
| 163 | |||
| 133 | Some(OutResponse::Accepted) | 164 | Some(OutResponse::Accepted) |
| 134 | } | 165 | } |
| 135 | REQ_SET_CONTROL_LINE_STATE => { | 166 | REQ_SET_CONTROL_LINE_STATE => { |
| @@ -141,6 +172,9 @@ impl<'d> Handler for Control<'d> { | |||
| 141 | shared.rts.store(rts, Ordering::Relaxed); | 172 | shared.rts.store(rts, Ordering::Relaxed); |
| 142 | debug!("Set dtr {}, rts {}", dtr, rts); | 173 | debug!("Set dtr {}, rts {}", dtr, rts); |
| 143 | 174 | ||
| 175 | shared.changed.store(true, Ordering::Relaxed); | ||
| 176 | shared.waker.borrow_mut().wake(); | ||
| 177 | |||
| 144 | Some(OutResponse::Accepted) | 178 | Some(OutResponse::Accepted) |
| 145 | } | 179 | } |
| 146 | _ => Some(OutResponse::Rejected), | 180 | _ => Some(OutResponse::Rejected), |
| @@ -292,6 +326,38 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | |||
| 292 | }, | 326 | }, |
| 293 | ) | 327 | ) |
| 294 | } | 328 | } |
| 329 | |||
| 330 | /// Split the class into sender, receiver and control | ||
| 331 | /// | ||
| 332 | /// Allows concurrently sending and receiving packets whilst monitoring for | ||
| 333 | /// control changes (dtr, rts) | ||
| 334 | pub fn split_with_control(self) -> (Sender<'d, D>, Receiver<'d, D>, ControlChanged<'d>) { | ||
| 335 | ( | ||
| 336 | Sender { | ||
| 337 | write_ep: self.write_ep, | ||
| 338 | control: self.control, | ||
| 339 | }, | ||
| 340 | Receiver { | ||
| 341 | read_ep: self.read_ep, | ||
| 342 | control: self.control, | ||
| 343 | }, | ||
| 344 | ControlChanged { control: self.control }, | ||
| 345 | ) | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | /// CDC ACM Control status change monitor | ||
| 350 | /// | ||
| 351 | /// You can obtain a `ControlChanged` with [`CdcAcmClass::split_with_control`] | ||
| 352 | pub struct ControlChanged<'d> { | ||
| 353 | control: &'d ControlShared, | ||
| 354 | } | ||
| 355 | |||
| 356 | impl<'d> ControlChanged<'d> { | ||
| 357 | /// Return a future for when the control settings change | ||
| 358 | pub async fn control_changed(&self) { | ||
| 359 | self.control.changed().await | ||
| 360 | } | ||
| 295 | } | 361 | } |
| 296 | 362 | ||
| 297 | /// CDC ACM class packet sender. | 363 | /// CDC ACM class packet sender. |
