aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralexmoon <[email protected]>2022-03-27 17:12:57 -0400
committerDario Nieuwenhuis <[email protected]>2022-04-06 05:38:11 +0200
commit52c622b1cd02ba13accc84cd57e90b04797f0405 (patch)
tree67057b66716715a75a2ca60ecf6115720d577961
parentbdc6e0481c42d20d5cca19dfc8ec56306e47296e (diff)
Use trait objects instead of generics for UsbDevice::classes
-rw-r--r--embassy-usb/src/builder.rs5
-rw-r--r--embassy-usb/src/class.rs147
-rw-r--r--embassy-usb/src/lib.rs48
-rw-r--r--examples/nrf/src/bin/usb/cdc_acm.rs117
-rw-r--r--examples/nrf/src/bin/usb/main.rs4
5 files changed, 120 insertions, 201 deletions
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs
index f0f94b932..bcb838ff5 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> {
175 } 175 }
176 176
177 /// Creates the [`UsbDevice`] instance with the configuration in this builder. 177 /// Creates the [`UsbDevice`] instance with the configuration in this builder.
178 /// 178 pub fn build(mut self, classes: &'d mut [&'d mut dyn UsbClass]) -> UsbDevice<'d, D> {
179 /// If a device has mutliple [`UsbClass`]es, they can be provided as a tuple list:
180 /// `(class1, (class2, (class3, ()))`.
181 pub fn build<C: UsbClass<'d, D>>(mut self, classes: C) -> UsbDevice<'d, D, C> {
182 self.config_descriptor.end_configuration(); 179 self.config_descriptor.end_configuration();
183 self.bos_descriptor.end_bos(); 180 self.bos_descriptor.end_bos();
184 181
diff --git a/embassy-usb/src/class.rs b/embassy-usb/src/class.rs
index 97bf7aba1..a0141e318 100644
--- a/embassy-usb/src/class.rs
+++ b/embassy-usb/src/class.rs
@@ -1,7 +1,4 @@
1use core::future::Future;
2
3use crate::control::Request; 1use crate::control::Request;
4use crate::driver::{ControlPipe, Driver};
5 2
6#[derive(Copy, Clone, Eq, PartialEq, Debug)] 3#[derive(Copy, Clone, Eq, PartialEq, Debug)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))] 4#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -21,19 +18,7 @@ impl Default for RequestStatus {
21/// 18///
22/// All methods are optional callbacks that will be called by 19/// All methods are optional callbacks that will be called by
23/// [`UsbDevice::run()`](crate::UsbDevice::run) 20/// [`UsbDevice::run()`](crate::UsbDevice::run)
24pub trait UsbClass<'d, D: Driver<'d>> { 21pub trait UsbClass {
25 type ControlOutFuture<'a>: Future<Output = RequestStatus> + 'a
26 where
27 Self: 'a,
28 'd: 'a,
29 D: 'a;
30
31 type ControlInFuture<'a>: Future<Output = ControlInRequestStatus> + 'a
32 where
33 Self: 'a,
34 'd: 'a,
35 D: 'a;
36
37 /// Called after a USB reset after the bus reset sequence is complete. 22 /// Called after a USB reset after the bus reset sequence is complete.
38 fn reset(&mut self) {} 23 fn reset(&mut self) {}
39 24
@@ -50,10 +35,7 @@ pub trait UsbClass<'d, D: Driver<'d>> {
50 /// 35 ///
51 /// * `req` - The request from the SETUP packet. 36 /// * `req` - The request from the SETUP packet.
52 /// * `data` - The data from the request. 37 /// * `data` - The data from the request.
53 fn control_out<'a>(&'a mut self, req: Request, data: &'a [u8]) -> Self::ControlOutFuture<'a> 38 fn control_out(&mut self, req: Request, data: &[u8]) -> RequestStatus;
54 where
55 'd: 'a,
56 D: 'a;
57 39
58 /// Called when a control request is received with direction DeviceToHost. 40 /// Called when a control request is received with direction DeviceToHost.
59 /// 41 ///
@@ -71,120 +53,63 @@ pub trait UsbClass<'d, D: Driver<'d>> {
71 /// * `req` - The request from the SETUP packet. 53 /// * `req` - The request from the SETUP packet.
72 /// * `control` - The control pipe. 54 /// * `control` - The control pipe.
73 fn control_in<'a>( 55 fn control_in<'a>(
74 &'a mut self, 56 &mut self,
75 req: Request, 57 req: Request,
76 control: ControlIn<'a, 'd, D>, 58 control: ControlIn<'a>,
77 ) -> Self::ControlInFuture<'a> 59 ) -> ControlInRequestStatus<'a>;
78 where
79 'd: 'a;
80}
81
82impl<'d, D: Driver<'d>> UsbClass<'d, D> for () {
83 type ControlOutFuture<'a> = impl Future<Output = RequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a;
84 type ControlInFuture<'a> = impl Future<Output = ControlInRequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a;
85
86 fn control_out<'a>(&'a mut self, _req: Request, _data: &'a [u8]) -> Self::ControlOutFuture<'a>
87 where
88 'd: 'a,
89 D: 'a,
90 {
91 async move { RequestStatus::default() }
92 }
93
94 fn control_in<'a>(
95 &'a mut self,
96 _req: Request,
97 control: ControlIn<'a, 'd, D>,
98 ) -> Self::ControlInFuture<'a>
99 where
100 'd: 'a,
101 D: 'a,
102 {
103 async move { control.ignore() }
104 }
105}
106
107impl<'d, D: Driver<'d>, Head, Tail> UsbClass<'d, D> for (Head, Tail)
108where
109 Head: UsbClass<'d, D>,
110 Tail: UsbClass<'d, D>,
111{
112 type ControlOutFuture<'a> = impl Future<Output = RequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a;
113 type ControlInFuture<'a> = impl Future<Output = ControlInRequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a;
114
115 fn control_out<'a>(&'a mut self, req: Request, data: &'a [u8]) -> Self::ControlOutFuture<'a>
116 where
117 'd: 'a,
118 D: 'a,
119 {
120 async move {
121 match self.0.control_out(req, data).await {
122 RequestStatus::Unhandled => self.1.control_out(req, data).await,
123 status => status,
124 }
125 }
126 }
127
128 fn control_in<'a>(
129 &'a mut self,
130 req: Request,
131 control: ControlIn<'a, 'd, D>,
132 ) -> Self::ControlInFuture<'a>
133 where
134 'd: 'a,
135 {
136 async move {
137 match self
138 .0
139 .control_in(req, ControlIn::new(control.control))
140 .await
141 {
142 ControlInRequestStatus(RequestStatus::Unhandled) => {
143 self.1.control_in(req, control).await
144 }
145 status => status,
146 }
147 }
148 }
149} 60}
150 61
151/// Handle for a control IN transfer. When implementing a class, use the methods of this object to 62/// Handle for a control IN transfer. When implementing a class, use the methods of this object to
152/// response to the transfer with either data or an error (STALL condition). To ignore the request 63/// response to the transfer with either data or an error (STALL condition). To ignore the request
153/// and pass it on to the next class, call [`Self::ignore()`]. 64/// and pass it on to the next class, call [`Self::ignore()`].
154pub struct ControlIn<'a, 'd: 'a, D: Driver<'d>> { 65pub struct ControlIn<'a> {
155 control: &'a mut D::ControlPipe, 66 buf: &'a mut [u8],
156} 67}
157 68
158#[derive(Eq, PartialEq, Debug)] 69#[derive(Eq, PartialEq, Debug)]
159#[cfg_attr(feature = "defmt", derive(defmt::Format))] 70#[cfg_attr(feature = "defmt", derive(defmt::Format))]
160pub struct ControlInRequestStatus(pub(crate) RequestStatus); 71pub struct ControlInRequestStatus<'a> {
72 pub(crate) status: RequestStatus,
73 pub(crate) data: &'a [u8],
74}
161 75
162impl ControlInRequestStatus { 76impl<'a> ControlInRequestStatus<'a> {
163 pub fn status(self) -> RequestStatus { 77 pub fn status(&self) -> RequestStatus {
164 self.0 78 self.status
165 } 79 }
166} 80}
167 81
168impl<'a, 'd: 'a, D: Driver<'d>> ControlIn<'a, 'd, D> { 82impl<'a> ControlIn<'a> {
169 pub(crate) fn new(control: &'a mut D::ControlPipe) -> Self { 83 pub(crate) fn new(buf: &'a mut [u8]) -> Self {
170 ControlIn { control } 84 ControlIn { buf }
171 } 85 }
172 86
173 /// Ignores the request and leaves it unhandled. 87 /// Ignores the request and leaves it unhandled.
174 pub fn ignore(self) -> ControlInRequestStatus { 88 pub fn ignore(self) -> ControlInRequestStatus<'a> {
175 ControlInRequestStatus(RequestStatus::Unhandled) 89 ControlInRequestStatus {
90 status: RequestStatus::Unhandled,
91 data: &[],
92 }
176 } 93 }
177 94
178 /// Accepts the transfer with the supplied buffer. 95 /// Accepts the transfer with the supplied buffer.
179 pub async fn accept(self, data: &[u8]) -> ControlInRequestStatus { 96 pub fn accept(self, data: &[u8]) -> ControlInRequestStatus<'a> {
180 self.control.accept_in(data).await; 97 assert!(data.len() < self.buf.len());
181 98
182 ControlInRequestStatus(RequestStatus::Accepted) 99 let buf = &mut self.buf[0..data.len()];
100 buf.copy_from_slice(data);
101
102 ControlInRequestStatus {
103 status: RequestStatus::Accepted,
104 data: buf,
105 }
183 } 106 }
184 107
185 /// Rejects the transfer by stalling the pipe. 108 /// Rejects the transfer by stalling the pipe.
186 pub fn reject(self) -> ControlInRequestStatus { 109 pub fn reject(self) -> ControlInRequestStatus<'a> {
187 self.control.reject(); 110 ControlInRequestStatus {
188 ControlInRequestStatus(RequestStatus::Rejected) 111 status: RequestStatus::Unhandled,
112 data: &[],
113 }
189 } 114 }
190} 115}
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index 4082868fb..ff3930afa 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -13,6 +13,8 @@ pub mod driver;
13pub mod types; 13pub mod types;
14mod util; 14mod util;
15 15
16use class::ControlInRequestStatus;
17
16use self::class::{RequestStatus, UsbClass}; 18use self::class::{RequestStatus, UsbClass};
17use self::control::*; 19use self::control::*;
18use self::descriptor::*; 20use self::descriptor::*;
@@ -51,7 +53,7 @@ pub const CONFIGURATION_VALUE: u8 = 1;
51/// The default value for bAlternateSetting for all interfaces. 53/// The default value for bAlternateSetting for all interfaces.
52pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; 54pub const DEFAULT_ALTERNATE_SETTING: u8 = 0;
53 55
54pub struct UsbDevice<'d, D: Driver<'d>, C: UsbClass<'d, D>> { 56pub struct UsbDevice<'d, D: Driver<'d>> {
55 bus: D::Bus, 57 bus: D::Bus,
56 control: D::ControlPipe, 58 control: D::ControlPipe,
57 59
@@ -65,17 +67,17 @@ pub struct UsbDevice<'d, D: Driver<'d>, C: UsbClass<'d, D>> {
65 self_powered: bool, 67 self_powered: bool,
66 pending_address: u8, 68 pending_address: u8,
67 69
68 classes: C, 70 classes: &'d mut [&'d mut dyn UsbClass],
69} 71}
70 72
71impl<'d, D: Driver<'d>, C: UsbClass<'d, D>> UsbDevice<'d, D, C> { 73impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
72 pub(crate) fn build( 74 pub(crate) fn build(
73 mut driver: D, 75 mut driver: D,
74 config: Config<'d>, 76 config: Config<'d>,
75 device_descriptor: &'d [u8], 77 device_descriptor: &'d [u8],
76 config_descriptor: &'d [u8], 78 config_descriptor: &'d [u8],
77 bos_descriptor: &'d [u8], 79 bos_descriptor: &'d [u8],
78 classes: C, 80 classes: &'d mut [&'d mut dyn UsbClass],
79 ) -> Self { 81 ) -> Self {
80 let control = driver 82 let control = driver
81 .alloc_control_pipe(config.max_packet_size_0 as u16) 83 .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> {
113 self.remote_wakeup_enabled = false; 115 self.remote_wakeup_enabled = false;
114 self.pending_address = 0; 116 self.pending_address = 0;
115 117
116 self.classes.reset(); 118 for c in self.classes.iter_mut() {
119 c.reset();
120 }
117 } 121 }
118 Event::Resume => {} 122 Event::Resume => {}
119 Event::Suspend => { 123 Event::Suspend => {
@@ -155,10 +159,12 @@ impl<'d, D: Driver<'d>, C: UsbClass<'d, D>> UsbDevice<'d, D, C> {
155 &[] 159 &[]
156 }; 160 };
157 161
158 match self.classes.control_out(req, data).await { 162 for c in self.classes.iter_mut() {
159 RequestStatus::Accepted => return self.control.accept(), 163 match c.control_out(req, data) {
160 RequestStatus::Rejected => return self.control.reject(), 164 RequestStatus::Accepted => return self.control.accept(),
161 RequestStatus::Unhandled => (), 165 RequestStatus::Rejected => return self.control.reject(),
166 RequestStatus::Unhandled => (),
167 }
162 } 168 }
163 } 169 }
164 170
@@ -233,14 +239,22 @@ impl<'d, D: Driver<'d>, C: UsbClass<'d, D>> UsbDevice<'d, D, C> {
233 } 239 }
234 240
235 async fn handle_control_in(&mut self, req: Request) { 241 async fn handle_control_in(&mut self, req: Request) {
236 match self 242 let mut buf = [0; 128];
237 .classes 243 for c in self.classes.iter_mut() {
238 .control_in(req, class::ControlIn::new(&mut self.control)) 244 match c.control_in(req, class::ControlIn::new(&mut buf)) {
239 .await 245 ControlInRequestStatus {
240 .status() 246 status: RequestStatus::Accepted,
241 { 247 data,
242 RequestStatus::Accepted | RequestStatus::Rejected => return, 248 } => return self.control.accept_in(data).await,
243 RequestStatus::Unhandled => (), 249 ControlInRequestStatus {
250 status: RequestStatus::Rejected,
251 ..
252 } => return self.control.reject(),
253 ControlInRequestStatus {
254 status: RequestStatus::Unhandled,
255 ..
256 } => (),
257 }
244 } 258 }
245 259
246 match req.request_type { 260 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 eebf89221..5e4abfea2 100644
--- a/examples/nrf/src/bin/usb/cdc_acm.rs
+++ b/examples/nrf/src/bin/usb/cdc_acm.rs
@@ -1,4 +1,3 @@
1use core::future::Future;
2use core::mem; 1use core::mem;
3use defmt::info; 2use defmt::info;
4use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass}; 3use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass};
@@ -56,89 +55,71 @@ pub struct CdcAcmControl {
56 pub rts: bool, 55 pub rts: bool,
57} 56}
58 57
59impl<'d, D: Driver<'d>> UsbClass<'d, D> for CdcAcmControl { 58impl UsbClass for CdcAcmControl {
60 type ControlOutFuture<'a> = impl Future<Output = RequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a;
61 type ControlInFuture<'a> = impl Future<Output = ControlInRequestStatus> + 'a where Self: 'a, 'd: 'a, D: 'a;
62
63 fn reset(&mut self) { 59 fn reset(&mut self) {
64 self.line_coding = LineCoding::default(); 60 self.line_coding = LineCoding::default();
65 self.dtr = false; 61 self.dtr = false;
66 self.rts = false; 62 self.rts = false;
67 } 63 }
68 64
69 fn control_out<'a>( 65 fn control_out(&mut self, req: control::Request, data: &[u8]) -> RequestStatus {
70 &'a mut self, 66 if !(req.request_type == control::RequestType::Class
71 req: control::Request, 67 && req.recipient == control::Recipient::Interface
72 data: &'a [u8], 68 && req.index == u8::from(self.comm_if) as u16)
73 ) -> Self::ControlOutFuture<'a> 69 {
74 where 70 return RequestStatus::Unhandled;
75 'd: 'a, 71 }
76 D: 'a, 72
77 { 73 match req.request {
78 async move { 74 REQ_SEND_ENCAPSULATED_COMMAND => {
79 if !(req.request_type == control::RequestType::Class 75 // We don't actually support encapsulated commands but pretend we do for standards
80 && req.recipient == control::Recipient::Interface 76 // compatibility.
81 && req.index == u8::from(self.comm_if) as u16) 77 RequestStatus::Accepted
82 {
83 return RequestStatus::Unhandled;
84 } 78 }
79 REQ_SET_LINE_CODING if data.len() >= 7 => {
80 self.line_coding.data_rate = u32::from_le_bytes(data[0..4].try_into().unwrap());
81 self.line_coding.stop_bits = data[4].into();
82 self.line_coding.parity_type = data[5].into();
83 self.line_coding.data_bits = data[6];
84 info!("Set line coding to: {:?}", self.line_coding);
85
86 RequestStatus::Accepted
87 }
88 REQ_SET_CONTROL_LINE_STATE => {
89 self.dtr = (req.value & 0x0001) != 0;
90 self.rts = (req.value & 0x0002) != 0;
91 info!("Set dtr {}, rts {}", self.dtr, self.rts);
85 92
86 match req.request { 93 RequestStatus::Accepted
87 REQ_SEND_ENCAPSULATED_COMMAND => {
88 // We don't actually support encapsulated commands but pretend we do for standards
89 // compatibility.
90 RequestStatus::Accepted
91 }
92 REQ_SET_LINE_CODING if data.len() >= 7 => {
93 self.line_coding.data_rate = u32::from_le_bytes(data[0..4].try_into().unwrap());
94 self.line_coding.stop_bits = data[4].into();
95 self.line_coding.parity_type = data[5].into();
96 self.line_coding.data_bits = data[6];
97 info!("Set line coding to: {:?}", self.line_coding);
98
99 RequestStatus::Accepted
100 }
101 REQ_SET_CONTROL_LINE_STATE => {
102 self.dtr = (req.value & 0x0001) != 0;
103 self.rts = (req.value & 0x0002) != 0;
104 info!("Set dtr {}, rts {}", self.dtr, self.rts);
105
106 RequestStatus::Accepted
107 }
108 _ => RequestStatus::Rejected,
109 } 94 }
95 _ => RequestStatus::Rejected,
110 } 96 }
111 } 97 }
112 98
113 fn control_in<'a>( 99 fn control_in<'a>(
114 &'a mut self, 100 &mut self,
115 req: Request, 101 req: Request,
116 control: embassy_usb::class::ControlIn<'a, 'd, D>, 102 control: embassy_usb::class::ControlIn<'a>,
117 ) -> Self::ControlInFuture<'a> 103 ) -> ControlInRequestStatus<'a> {
118 where 104 if !(req.request_type == control::RequestType::Class
119 'd: 'a, 105 && req.recipient == control::Recipient::Interface
120 { 106 && req.index == u8::from(self.comm_if) as u16)
121 async move { 107 {
122 if !(req.request_type == control::RequestType::Class 108 return control.ignore();
123 && req.recipient == control::Recipient::Interface 109 }
124 && req.index == u8::from(self.comm_if) as u16)
125 {
126 return control.ignore();
127 }
128 110
129 match req.request { 111 match req.request {
130 // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. 112 // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
131 REQ_GET_LINE_CODING if req.length == 7 => { 113 REQ_GET_LINE_CODING if req.length == 7 => {
132 info!("Sending line coding"); 114 info!("Sending line coding");
133 let mut data = [0; 7]; 115 let mut data = [0; 7];
134 data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); 116 data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
135 data[4] = self.line_coding.stop_bits as u8; 117 data[4] = self.line_coding.stop_bits as u8;
136 data[5] = self.line_coding.parity_type as u8; 118 data[5] = self.line_coding.parity_type as u8;
137 data[6] = self.line_coding.data_bits; 119 data[6] = self.line_coding.data_bits;
138 control.accept(&data).await 120 control.accept(&data)
139 }
140 _ => control.reject(),
141 } 121 }
122 _ => control.reject(),
142 } 123 }
143 } 124 }
144} 125}
diff --git a/examples/nrf/src/bin/usb/main.rs b/examples/nrf/src/bin/usb/main.rs
index 71285579c..73ac3a21f 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;
16use embassy_nrf::pac; 16use embassy_nrf::pac;
17use embassy_nrf::usb::Driver; 17use embassy_nrf::usb::Driver;
18use embassy_nrf::Peripherals; 18use embassy_nrf::Peripherals;
19use embassy_usb::class::UsbClass;
19use embassy_usb::driver::{EndpointIn, EndpointOut}; 20use embassy_usb::driver::{EndpointIn, EndpointOut};
20use embassy_usb::{Config, UsbDeviceBuilder}; 21use embassy_usb::{Config, UsbDeviceBuilder};
21use futures::future::join3; 22use futures::future::join3;
@@ -59,7 +60,8 @@ async fn main(_spawner: Spawner, p: Peripherals) {
59 let mut class = CdcAcmClass::new(&mut builder, 64); 60 let mut class = CdcAcmClass::new(&mut builder, 64);
60 61
61 // Build the builder. 62 // Build the builder.
62 let mut usb = builder.build(class.control); 63 let mut classes: [&mut dyn UsbClass; 1] = [&mut class.control];
64 let mut usb = builder.build(&mut classes);
63 65
64 // Run the USB device. 66 // Run the USB device.
65 let fut1 = usb.run(); 67 let fut1 = usb.run();