usb: unify ControlHandler+DeviceStateHandler, route all control requests to all handlers.

- Allows classes to handle vendor requests.
- Allows classes to use a single handler for multiple interfaces.
- Allows classes to access the other events (previously only `reset` was available).
This commit is contained in:
Dario Nieuwenhuis
2023-02-07 22:49:14 +01:00
parent 1d841cc8ac
commit 3af991ab63
31 changed files with 381 additions and 338 deletions

View File

@ -6,10 +6,10 @@ use core::sync::atomic::{AtomicBool, Ordering};
use embassy_sync::blocking_mutex::CriticalSectionMutex;
use crate::control::{self, ControlHandler, InResponse, OutResponse, Request};
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::*;
use crate::Builder;
use crate::{Builder, Handler};
/// This should be used as `device_class` when building the `UsbDevice`.
pub const USB_CLASS_CDC: u8 = 0x02;
@ -67,6 +67,7 @@ pub struct CdcAcmClass<'d, D: Driver<'d>> {
}
struct Control<'a> {
comm_if: InterfaceNumber,
shared: &'a ControlShared,
}
@ -98,7 +99,7 @@ impl<'a> Control<'a> {
}
}
impl<'d> ControlHandler for Control<'d> {
impl<'d> Handler for Control<'d> {
fn reset(&mut self) {
let shared = self.shared();
shared.line_coding.lock(|x| x.set(LineCoding::default()));
@ -106,12 +107,18 @@ impl<'d> ControlHandler for Control<'d> {
shared.rts.store(false, Ordering::Relaxed);
}
fn control_out(&mut self, req: control::Request, data: &[u8]) -> OutResponse {
fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {
if (req.request_type, req.recipient, req.index)
!= (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)
{
return None;
}
match req.request {
REQ_SEND_ENCAPSULATED_COMMAND => {
// We don't actually support encapsulated commands but pretend we do for standards
// compatibility.
OutResponse::Accepted
Some(OutResponse::Accepted)
}
REQ_SET_LINE_CODING if data.len() >= 7 => {
let coding = LineCoding {
@ -123,7 +130,7 @@ impl<'d> ControlHandler for Control<'d> {
self.shared().line_coding.lock(|x| x.set(coding));
debug!("Set line coding to: {:?}", coding);
OutResponse::Accepted
Some(OutResponse::Accepted)
}
REQ_SET_CONTROL_LINE_STATE => {
let dtr = (req.value & 0x0001) != 0;
@ -134,13 +141,19 @@ impl<'d> ControlHandler for Control<'d> {
shared.rts.store(rts, Ordering::Relaxed);
debug!("Set dtr {}, rts {}", dtr, rts);
OutResponse::Accepted
Some(OutResponse::Accepted)
}
_ => OutResponse::Rejected,
_ => Some(OutResponse::Rejected),
}
}
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
if (req.request_type, req.recipient, req.index)
!= (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)
{
return None;
}
match req.request {
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
REQ_GET_LINE_CODING if req.length == 7 => {
@ -151,9 +164,9 @@ impl<'d> ControlHandler for Control<'d> {
buf[4] = coding.stop_bits as u8;
buf[5] = coding.parity_type as u8;
buf[6] = coding.data_bits;
InResponse::Accepted(&buf[0..7])
Some(InResponse::Accepted(&buf[0..7]))
}
_ => InResponse::Rejected,
_ => Some(InResponse::Rejected),
}
}
}
@ -162,17 +175,12 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
/// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self {
let control = state.control.write(Control { shared: &state.shared });
let control_shared = &state.shared;
assert!(builder.control_buf_len() >= 7);
let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE);
// Control interface
let mut iface = func.interface();
iface.handler(control);
let comm_if = iface.interface_number();
let data_if = u8::from(comm_if) + 1;
let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None);
@ -213,6 +221,16 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
let read_ep = alt.endpoint_bulk_out(max_packet_size);
let write_ep = alt.endpoint_bulk_in(max_packet_size);
drop(func);
let control = state.control.write(Control {
shared: &state.shared,
comm_if,
});
builder.handler(control);
let control_shared = &state.shared;
CdcAcmClass {
_comm_ep: comm_ep,
_data_if: data_if,