diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index f0f94b93..bcb838ff 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -175,10 +175,7 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> { } /// Creates the [`UsbDevice`] instance with the configuration in this builder. - /// - /// If a device has mutliple [`UsbClass`]es, they can be provided as a tuple list: - /// `(class1, (class2, (class3, ()))`. - pub fn build>(mut self, classes: C) -> UsbDevice<'d, D, C> { + pub fn build(mut self, classes: &'d mut [&'d mut dyn UsbClass]) -> UsbDevice<'d, D> { self.config_descriptor.end_configuration(); self.bos_descriptor.end_bos(); diff --git a/embassy-usb/src/class.rs b/embassy-usb/src/class.rs index 97bf7aba..a0141e31 100644 --- a/embassy-usb/src/class.rs +++ b/embassy-usb/src/class.rs @@ -1,7 +1,4 @@ -use core::future::Future; - use crate::control::Request; -use crate::driver::{ControlPipe, Driver}; #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -21,19 +18,7 @@ impl Default for RequestStatus { /// /// All methods are optional callbacks that will be called by /// [`UsbDevice::run()`](crate::UsbDevice::run) -pub trait UsbClass<'d, D: Driver<'d>> { - type ControlOutFuture<'a>: Future + 'a - where - Self: 'a, - 'd: 'a, - D: 'a; - - type ControlInFuture<'a>: Future + 'a - where - Self: 'a, - 'd: 'a, - D: 'a; - +pub trait UsbClass { /// Called after a USB reset after the bus reset sequence is complete. fn reset(&mut self) {} @@ -50,10 +35,7 @@ pub trait UsbClass<'d, D: Driver<'d>> { /// /// * `req` - The request from the SETUP packet. /// * `data` - The data from the request. - fn control_out<'a>(&'a mut self, req: Request, data: &'a [u8]) -> Self::ControlOutFuture<'a> - where - 'd: 'a, - D: 'a; + fn control_out(&mut self, req: Request, data: &[u8]) -> RequestStatus; /// Called when a control request is received with direction DeviceToHost. /// @@ -71,120 +53,63 @@ pub trait UsbClass<'d, D: Driver<'d>> { /// * `req` - The request from the SETUP packet. /// * `control` - The control pipe. fn control_in<'a>( - &'a mut self, + &mut self, req: Request, - control: ControlIn<'a, 'd, D>, - ) -> Self::ControlInFuture<'a> - where - 'd: 'a; -} - -impl<'d, D: Driver<'d>> UsbClass<'d, D> for () { - type ControlOutFuture<'a> = impl Future + 'a where Self: 'a, 'd: 'a, D: 'a; - type ControlInFuture<'a> = impl Future + 'a where Self: 'a, 'd: 'a, D: 'a; - - fn control_out<'a>(&'a mut self, _req: Request, _data: &'a [u8]) -> Self::ControlOutFuture<'a> - where - 'd: 'a, - D: 'a, - { - async move { RequestStatus::default() } - } - - fn control_in<'a>( - &'a mut self, - _req: Request, - control: ControlIn<'a, 'd, D>, - ) -> Self::ControlInFuture<'a> - where - 'd: 'a, - D: 'a, - { - async move { control.ignore() } - } -} - -impl<'d, D: Driver<'d>, Head, Tail> UsbClass<'d, D> for (Head, Tail) -where - Head: UsbClass<'d, D>, - Tail: UsbClass<'d, D>, -{ - type ControlOutFuture<'a> = impl Future + 'a where Self: 'a, 'd: 'a, D: 'a; - type ControlInFuture<'a> = impl Future + 'a where Self: 'a, 'd: 'a, D: 'a; - - fn control_out<'a>(&'a mut self, req: Request, data: &'a [u8]) -> Self::ControlOutFuture<'a> - where - 'd: 'a, - D: 'a, - { - async move { - match self.0.control_out(req, data).await { - RequestStatus::Unhandled => self.1.control_out(req, data).await, - status => status, - } - } - } - - fn control_in<'a>( - &'a mut self, - req: Request, - control: ControlIn<'a, 'd, D>, - ) -> Self::ControlInFuture<'a> - where - 'd: 'a, - { - async move { - match self - .0 - .control_in(req, ControlIn::new(control.control)) - .await - { - ControlInRequestStatus(RequestStatus::Unhandled) => { - self.1.control_in(req, control).await - } - status => status, - } - } - } + control: ControlIn<'a>, + ) -> ControlInRequestStatus<'a>; } /// Handle for a control IN transfer. When implementing a class, use the methods of this object to /// response to the transfer with either data or an error (STALL condition). To ignore the request /// and pass it on to the next class, call [`Self::ignore()`]. -pub struct ControlIn<'a, 'd: 'a, D: Driver<'d>> { - control: &'a mut D::ControlPipe, +pub struct ControlIn<'a> { + buf: &'a mut [u8], } #[derive(Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ControlInRequestStatus(pub(crate) RequestStatus); +pub struct ControlInRequestStatus<'a> { + pub(crate) status: RequestStatus, + pub(crate) data: &'a [u8], +} -impl ControlInRequestStatus { - pub fn status(self) -> RequestStatus { - self.0 +impl<'a> ControlInRequestStatus<'a> { + pub fn status(&self) -> RequestStatus { + self.status } } -impl<'a, 'd: 'a, D: Driver<'d>> ControlIn<'a, 'd, D> { - pub(crate) fn new(control: &'a mut D::ControlPipe) -> Self { - ControlIn { control } +impl<'a> ControlIn<'a> { + pub(crate) fn new(buf: &'a mut [u8]) -> Self { + ControlIn { buf } } /// Ignores the request and leaves it unhandled. - pub fn ignore(self) -> ControlInRequestStatus { - ControlInRequestStatus(RequestStatus::Unhandled) + pub fn ignore(self) -> ControlInRequestStatus<'a> { + ControlInRequestStatus { + status: RequestStatus::Unhandled, + data: &[], + } } /// Accepts the transfer with the supplied buffer. - pub async fn accept(self, data: &[u8]) -> ControlInRequestStatus { - self.control.accept_in(data).await; + pub fn accept(self, data: &[u8]) -> ControlInRequestStatus<'a> { + assert!(data.len() < self.buf.len()); - ControlInRequestStatus(RequestStatus::Accepted) + let buf = &mut self.buf[0..data.len()]; + buf.copy_from_slice(data); + + ControlInRequestStatus { + status: RequestStatus::Accepted, + data: buf, + } } /// Rejects the transfer by stalling the pipe. - pub fn reject(self) -> ControlInRequestStatus { - self.control.reject(); - ControlInRequestStatus(RequestStatus::Rejected) + pub fn reject(self) -> ControlInRequestStatus<'a> { + ControlInRequestStatus { + status: RequestStatus::Unhandled, + data: &[], + } } } diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 4082868f..ff3930af 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -13,6 +13,8 @@ pub mod driver; pub mod types; mod util; +use class::ControlInRequestStatus; + use self::class::{RequestStatus, UsbClass}; use self::control::*; use self::descriptor::*; @@ -51,7 +53,7 @@ pub const CONFIGURATION_VALUE: u8 = 1; /// The default value for bAlternateSetting for all interfaces. pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; -pub struct UsbDevice<'d, D: Driver<'d>, C: UsbClass<'d, D>> { +pub struct UsbDevice<'d, D: Driver<'d>> { bus: D::Bus, control: D::ControlPipe, @@ -65,17 +67,17 @@ pub struct UsbDevice<'d, D: Driver<'d>, C: UsbClass<'d, D>> { self_powered: bool, pending_address: u8, - classes: C, + classes: &'d mut [&'d mut dyn UsbClass], } -impl<'d, D: Driver<'d>, C: UsbClass<'d, D>> UsbDevice<'d, D, C> { +impl<'d, D: Driver<'d>> UsbDevice<'d, D> { pub(crate) fn build( mut driver: D, config: Config<'d>, device_descriptor: &'d [u8], config_descriptor: &'d [u8], bos_descriptor: &'d [u8], - classes: C, + classes: &'d mut [&'d mut dyn UsbClass], ) -> Self { let control = driver .alloc_control_pipe(config.max_packet_size_0 as u16) @@ -113,7 +115,9 @@ impl<'d, D: Driver<'d>, C: UsbClass<'d, D>> UsbDevice<'d, D, C> { self.remote_wakeup_enabled = false; self.pending_address = 0; - self.classes.reset(); + for c in self.classes.iter_mut() { + c.reset(); + } } Event::Resume => {} Event::Suspend => { @@ -155,10 +159,12 @@ impl<'d, D: Driver<'d>, C: UsbClass<'d, D>> UsbDevice<'d, D, C> { &[] }; - match self.classes.control_out(req, data).await { - RequestStatus::Accepted => return self.control.accept(), - RequestStatus::Rejected => return self.control.reject(), - RequestStatus::Unhandled => (), + for c in self.classes.iter_mut() { + match c.control_out(req, data) { + RequestStatus::Accepted => return self.control.accept(), + RequestStatus::Rejected => return self.control.reject(), + RequestStatus::Unhandled => (), + } } } @@ -233,14 +239,22 @@ impl<'d, D: Driver<'d>, C: UsbClass<'d, D>> UsbDevice<'d, D, C> { } async fn handle_control_in(&mut self, req: Request) { - match self - .classes - .control_in(req, class::ControlIn::new(&mut self.control)) - .await - .status() - { - RequestStatus::Accepted | RequestStatus::Rejected => return, - RequestStatus::Unhandled => (), + let mut buf = [0; 128]; + for c in self.classes.iter_mut() { + match c.control_in(req, class::ControlIn::new(&mut buf)) { + ControlInRequestStatus { + status: RequestStatus::Accepted, + data, + } => return self.control.accept_in(data).await, + ControlInRequestStatus { + status: RequestStatus::Rejected, + .. + } => return self.control.reject(), + ControlInRequestStatus { + status: RequestStatus::Unhandled, + .. + } => (), + } } match req.request_type { diff --git a/examples/nrf/src/bin/usb/cdc_acm.rs b/examples/nrf/src/bin/usb/cdc_acm.rs index eebf8922..5e4abfea 100644 --- a/examples/nrf/src/bin/usb/cdc_acm.rs +++ b/examples/nrf/src/bin/usb/cdc_acm.rs @@ -1,4 +1,3 @@ -use core::future::Future; use core::mem; use defmt::info; use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass}; @@ -56,89 +55,71 @@ pub struct CdcAcmControl { pub rts: bool, } -impl<'d, D: Driver<'d>> UsbClass<'d, D> for CdcAcmControl { - type ControlOutFuture<'a> = impl Future + 'a where Self: 'a, 'd: 'a, D: 'a; - type ControlInFuture<'a> = impl Future + 'a where Self: 'a, 'd: 'a, D: 'a; - +impl UsbClass for CdcAcmControl { fn reset(&mut self) { self.line_coding = LineCoding::default(); self.dtr = false; self.rts = false; } - fn control_out<'a>( - &'a mut self, - req: control::Request, - data: &'a [u8], - ) -> Self::ControlOutFuture<'a> - where - 'd: 'a, - D: 'a, - { - async move { - if !(req.request_type == control::RequestType::Class - && req.recipient == control::Recipient::Interface - && req.index == u8::from(self.comm_if) as u16) - { - return RequestStatus::Unhandled; + fn control_out(&mut self, req: control::Request, data: &[u8]) -> RequestStatus { + if !(req.request_type == control::RequestType::Class + && req.recipient == control::Recipient::Interface + && req.index == u8::from(self.comm_if) as u16) + { + return RequestStatus::Unhandled; + } + + match req.request { + REQ_SEND_ENCAPSULATED_COMMAND => { + // We don't actually support encapsulated commands but pretend we do for standards + // compatibility. + RequestStatus::Accepted } + REQ_SET_LINE_CODING if data.len() >= 7 => { + self.line_coding.data_rate = u32::from_le_bytes(data[0..4].try_into().unwrap()); + self.line_coding.stop_bits = data[4].into(); + self.line_coding.parity_type = data[5].into(); + self.line_coding.data_bits = data[6]; + info!("Set line coding to: {:?}", self.line_coding); - match req.request { - REQ_SEND_ENCAPSULATED_COMMAND => { - // We don't actually support encapsulated commands but pretend we do for standards - // compatibility. - RequestStatus::Accepted - } - REQ_SET_LINE_CODING if data.len() >= 7 => { - self.line_coding.data_rate = u32::from_le_bytes(data[0..4].try_into().unwrap()); - self.line_coding.stop_bits = data[4].into(); - self.line_coding.parity_type = data[5].into(); - self.line_coding.data_bits = data[6]; - info!("Set line coding to: {:?}", self.line_coding); - - RequestStatus::Accepted - } - REQ_SET_CONTROL_LINE_STATE => { - self.dtr = (req.value & 0x0001) != 0; - self.rts = (req.value & 0x0002) != 0; - info!("Set dtr {}, rts {}", self.dtr, self.rts); - - RequestStatus::Accepted - } - _ => RequestStatus::Rejected, + RequestStatus::Accepted } + REQ_SET_CONTROL_LINE_STATE => { + self.dtr = (req.value & 0x0001) != 0; + self.rts = (req.value & 0x0002) != 0; + info!("Set dtr {}, rts {}", self.dtr, self.rts); + + RequestStatus::Accepted + } + _ => RequestStatus::Rejected, } } fn control_in<'a>( - &'a mut self, + &mut self, req: Request, - control: embassy_usb::class::ControlIn<'a, 'd, D>, - ) -> Self::ControlInFuture<'a> - where - 'd: 'a, - { - async move { - if !(req.request_type == control::RequestType::Class - && req.recipient == control::Recipient::Interface - && req.index == u8::from(self.comm_if) as u16) - { - return control.ignore(); - } + control: embassy_usb::class::ControlIn<'a>, + ) -> ControlInRequestStatus<'a> { + if !(req.request_type == control::RequestType::Class + && req.recipient == control::Recipient::Interface + && req.index == u8::from(self.comm_if) as u16) + { + return control.ignore(); + } - match req.request { - // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. - REQ_GET_LINE_CODING if req.length == 7 => { - info!("Sending line coding"); - let mut data = [0; 7]; - data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); - data[4] = self.line_coding.stop_bits as u8; - data[5] = self.line_coding.parity_type as u8; - data[6] = self.line_coding.data_bits; - control.accept(&data).await - } - _ => control.reject(), + match req.request { + // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. + REQ_GET_LINE_CODING if req.length == 7 => { + info!("Sending line coding"); + let mut data = [0; 7]; + data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); + data[4] = self.line_coding.stop_bits as u8; + data[5] = self.line_coding.parity_type as u8; + data[6] = self.line_coding.data_bits; + control.accept(&data) } + _ => control.reject(), } } } diff --git a/examples/nrf/src/bin/usb/main.rs b/examples/nrf/src/bin/usb/main.rs index 71285579..73ac3a21 100644 --- a/examples/nrf/src/bin/usb/main.rs +++ b/examples/nrf/src/bin/usb/main.rs @@ -16,6 +16,7 @@ use embassy_nrf::interrupt; use embassy_nrf::pac; use embassy_nrf::usb::Driver; use embassy_nrf::Peripherals; +use embassy_usb::class::UsbClass; use embassy_usb::driver::{EndpointIn, EndpointOut}; use embassy_usb::{Config, UsbDeviceBuilder}; use futures::future::join3; @@ -59,7 +60,8 @@ async fn main(_spawner: Spawner, p: Peripherals) { let mut class = CdcAcmClass::new(&mut builder, 64); // Build the builder. - let mut usb = builder.build(class.control); + let mut classes: [&mut dyn UsbClass; 1] = [&mut class.control]; + let mut usb = builder.build(&mut classes); // Run the USB device. let fut1 = usb.run();