Merge pull request #1926 from m5p3nc3r/async_dtr

Add a signal for when the CDC control state changes
This commit is contained in:
Dario Nieuwenhuis 2023-10-03 02:13:12 +00:00 committed by GitHub
commit ad524373ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,10 +1,13 @@
//! CDC-ACM class implementation, aka Serial over USB. //! 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::mem::{self, MaybeUninit};
use core::sync::atomic::{AtomicBool, Ordering}; use core::sync::atomic::{AtomicBool, Ordering};
use core::task::Poll;
use embassy_sync::blocking_mutex::CriticalSectionMutex; use embassy_sync::blocking_mutex::CriticalSectionMutex;
use embassy_sync::waitqueue::WakerRegistration;
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
@ -76,6 +79,9 @@ struct ControlShared {
line_coding: CriticalSectionMutex<Cell<LineCoding>>, line_coding: CriticalSectionMutex<Cell<LineCoding>>,
dtr: AtomicBool, dtr: AtomicBool,
rts: AtomicBool, rts: AtomicBool,
waker: RefCell<WakerRegistration>,
changed: AtomicBool,
} }
impl Default for ControlShared { impl Default for ControlShared {
@ -89,10 +95,28 @@ impl Default for ControlShared {
parity_type: ParityType::None, parity_type: ParityType::None,
data_rate: 8_000, 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> { impl<'a> Control<'a> {
fn shared(&mut self) -> &'a ControlShared { fn shared(&mut self) -> &'a ControlShared {
self.shared self.shared
@ -105,6 +129,9 @@ impl<'d> Handler for Control<'d> {
shared.line_coding.lock(|x| x.set(LineCoding::default())); shared.line_coding.lock(|x| x.set(LineCoding::default()));
shared.dtr.store(false, Ordering::Relaxed); shared.dtr.store(false, Ordering::Relaxed);
shared.rts.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<OutResponse> { fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {
@ -127,9 +154,13 @@ impl<'d> Handler for Control<'d> {
parity_type: data[5].into(), parity_type: data[5].into(),
data_bits: data[6], 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); debug!("Set line coding to: {:?}", coding);
shared.changed.store(true, Ordering::Relaxed);
shared.waker.borrow_mut().wake();
Some(OutResponse::Accepted) Some(OutResponse::Accepted)
} }
REQ_SET_CONTROL_LINE_STATE => { REQ_SET_CONTROL_LINE_STATE => {
@ -141,6 +172,9 @@ impl<'d> Handler for Control<'d> {
shared.rts.store(rts, Ordering::Relaxed); shared.rts.store(rts, Ordering::Relaxed);
debug!("Set dtr {}, rts {}", dtr, rts); debug!("Set dtr {}, rts {}", dtr, rts);
shared.changed.store(true, Ordering::Relaxed);
shared.waker.borrow_mut().wake();
Some(OutResponse::Accepted) Some(OutResponse::Accepted)
} }
_ => Some(OutResponse::Rejected), _ => Some(OutResponse::Rejected),
@ -292,6 +326,38 @@ 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 },
)
}
}
/// CDC ACM Control status change monitor
///
/// You can obtain a `ControlChanged` with [`CdcAcmClass::split_with_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. /// CDC ACM class packet sender.