Merge pull request #1926 from m5p3nc3r/async_dtr
Add a signal for when the CDC control state changes
This commit is contained in:
commit
ad524373ed
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user