From ae24da0ef2ed1b8718244e5face2d0ca257146d5 Mon Sep 17 00:00:00 2001 From: Matt Spencer Date: Mon, 18 Sep 2023 15:10:03 +0100 Subject: [PATCH 1/3] Add a signal for when the CDC control state changes This allows an async wait for changes in dts for example. Signed-off-by: Matt Spencer --- embassy-usb/src/class/cdc_acm.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index a341e10da0..b2371170f0 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -5,6 +5,8 @@ use core::mem::{self, MaybeUninit}; use core::sync::atomic::{AtomicBool, Ordering}; use embassy_sync::blocking_mutex::CriticalSectionMutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::signal::Signal; use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; @@ -30,6 +32,9 @@ const REQ_SET_LINE_CODING: u8 = 0x20; const REQ_GET_LINE_CODING: u8 = 0x21; const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; +/// Signal will be send whenever the CDC control state changes +pub static CDC_CONTROL_CHANGED: Signal = Signal::new(); + /// Internal state for CDC-ACM pub struct State<'a> { control: MaybeUninit>, @@ -105,6 +110,8 @@ impl<'d> Handler for Control<'d> { shared.line_coding.lock(|x| x.set(LineCoding::default())); shared.dtr.store(false, Ordering::Relaxed); shared.rts.store(false, Ordering::Relaxed); + + CDC_CONTROL_CHANGED.signal(()); } fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option { @@ -141,6 +148,8 @@ impl<'d> Handler for Control<'d> { shared.rts.store(rts, Ordering::Relaxed); debug!("Set dtr {}, rts {}", dtr, rts); + CDC_CONTROL_CHANGED.signal(()); + Some(OutResponse::Accepted) } _ => Some(OutResponse::Rejected), From 135415a456b9264f745cc1819f5ad16deae01ec5 Mon Sep 17 00:00:00 2001 From: Matt Spencer Date: Mon, 18 Sep 2023 15:10:03 +0100 Subject: [PATCH 2/3] Use waker for shared state Allows async on each CDC instance. Signed-off-by: Matt Spencer matthew@thespencers.me.uk --- embassy-usb/src/class/cdc_acm.rs | 67 +++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index a341e10da0..23e0edc240 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -1,10 +1,13 @@ //! CDC-ACM class implementation, aka Serial over USB. -use core::cell::Cell; +use core::cell::{Cell, RefCell}; +use core::future::poll_fn; use core::mem::{self, MaybeUninit}; use core::sync::atomic::{AtomicBool, Ordering}; +use core::task::Poll; use embassy_sync::blocking_mutex::CriticalSectionMutex; +use embassy_sync::waitqueue::WakerRegistration; use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; @@ -76,6 +79,9 @@ struct ControlShared { line_coding: CriticalSectionMutex>, dtr: AtomicBool, rts: AtomicBool, + + waker: RefCell, + changed: AtomicBool, } impl Default for ControlShared { @@ -89,10 +95,28 @@ impl Default for ControlShared { parity_type: ParityType::None, data_rate: 8_000, })), + waker: RefCell::new(WakerRegistration::new()), + changed: AtomicBool::new(false), } } } +impl ControlShared { + async fn changed(&self) { + poll_fn(|cx| match self.changed.load(Ordering::Relaxed) { + true => { + self.changed.store(false, Ordering::Relaxed); + Poll::Ready(()) + } + false => { + self.waker.borrow_mut().register(cx.waker()); + Poll::Pending + } + }) + .await + } +} + impl<'a> Control<'a> { fn shared(&mut self) -> &'a ControlShared { self.shared @@ -105,6 +129,9 @@ impl<'d> Handler for Control<'d> { shared.line_coding.lock(|x| x.set(LineCoding::default())); shared.dtr.store(false, Ordering::Relaxed); shared.rts.store(false, Ordering::Relaxed); + + shared.changed.store(true, Ordering::Relaxed); + shared.waker.borrow_mut().wake(); } fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option { @@ -127,9 +154,13 @@ impl<'d> Handler for Control<'d> { parity_type: data[5].into(), data_bits: data[6], }; - self.shared().line_coding.lock(|x| x.set(coding)); + let shared = self.shared(); + shared.line_coding.lock(|x| x.set(coding)); debug!("Set line coding to: {:?}", coding); + shared.changed.store(true, Ordering::Relaxed); + shared.waker.borrow_mut().wake(); + Some(OutResponse::Accepted) } REQ_SET_CONTROL_LINE_STATE => { @@ -141,6 +172,9 @@ impl<'d> Handler for Control<'d> { shared.rts.store(rts, Ordering::Relaxed); debug!("Set dtr {}, rts {}", dtr, rts); + shared.changed.store(true, Ordering::Relaxed); + shared.waker.borrow_mut().wake(); + Some(OutResponse::Accepted) } _ => Some(OutResponse::Rejected), @@ -292,6 +326,35 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { }, ) } + + /// Split the class into sender, receiver and control + /// + /// Allows concurrently sending and receiving packets whilst monitoring for + /// control changes (dtr, rts) + pub fn split_with_control(self) -> (Sender<'d, D>, Receiver<'d, D>, ControlChanged<'d>) { + ( + Sender { + write_ep: self.write_ep, + control: self.control, + }, + Receiver { + read_ep: self.read_ep, + control: self.control, + }, + ControlChanged { control: self.control }, + ) + } +} + +pub struct ControlChanged<'d> { + control: &'d ControlShared, +} + +impl<'d> ControlChanged<'d> { + /// Return a future for when the control settings change + pub async fn control_changed(&self) { + self.control.changed().await + } } /// CDC ACM class packet sender. From 243a3abb33a75494ba9e906906e4925c8bc49b42 Mon Sep 17 00:00:00 2001 From: Matt Spencer Date: Mon, 18 Sep 2023 22:38:11 +0100 Subject: [PATCH 3/3] Remove --- embassy-usb/src/class/cdc_acm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 910b1236b9..0c708464d9 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -33,9 +33,6 @@ const REQ_SET_LINE_CODING: u8 = 0x20; const REQ_GET_LINE_CODING: u8 = 0x21; const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; -/// Signal will be send whenever the CDC control state changes -pub static CDC_CONTROL_CHANGED: Signal = Signal::new(); - /// Internal state for CDC-ACM pub struct State<'a> { control: MaybeUninit>, @@ -349,6 +346,9 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { } } +/// CDC ACM Control status change monitor +/// +/// You can obtain a `ControlChanged` with [`CdcAcmClass::split_with_control`] pub struct ControlChanged<'d> { control: &'d ControlShared, }