Add async interface for CDC control changes

Signed-off-by: Matt Spencer <matthew@thespencers.me.uk>
This commit is contained in:
Matt Spencer 2023-09-19 15:42:26 +01:00
parent 183824fbdd
commit a402aed3d1

View File

@ -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<Cell<LineCoding>>,
dtr: AtomicBool,
rts: AtomicBool,
waker: RefCell<WakerRegistration>,
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<OutResponse> {
@ -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,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.