aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-03-28 02:20:01 +0200
committerDario Nieuwenhuis <[email protected]>2022-04-06 05:38:11 +0200
commita2f5763a67441e5e6c981407938c21c58e9eb669 (patch)
tree48f5d58eaf04e6a85da706e6960ac96cf5cd02ab
parenta062baae3837b453f0c05d0730ba465c6d2c2446 (diff)
usb: add `add_class` to builder, so that `FooBarClass::new(&mut builder)` can set up everything.
-rw-r--r--embassy-usb/Cargo.toml1
-rw-r--r--embassy-usb/src/builder.rs15
-rw-r--r--embassy-usb/src/lib.rs7
-rw-r--r--examples/nrf/src/bin/usb/cdc_acm.rs207
-rw-r--r--examples/nrf/src/bin/usb/main.rs11
5 files changed, 110 insertions, 131 deletions
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml
index 5a5a6d7ab..af5986c15 100644
--- a/embassy-usb/Cargo.toml
+++ b/embassy-usb/Cargo.toml
@@ -10,3 +10,4 @@ defmt = { version = "0.3", optional = true }
10log = { version = "0.4.14", optional = true } 10log = { version = "0.4.14", optional = true }
11cortex-m = "0.7.3" 11cortex-m = "0.7.3"
12num-traits = { version = "0.2.14", default-features = false } 12num-traits = { version = "0.2.14", default-features = false }
13heapless = "0.7.10" \ No newline at end of file
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs
index bcb838ff5..491acf4d3 100644
--- a/embassy-usb/src/builder.rs
+++ b/embassy-usb/src/builder.rs
@@ -1,8 +1,11 @@
1use heapless::Vec;
2
1use super::class::UsbClass; 3use super::class::UsbClass;
2use super::descriptor::{BosWriter, DescriptorWriter}; 4use super::descriptor::{BosWriter, DescriptorWriter};
3use super::driver::{Driver, EndpointAllocError}; 5use super::driver::{Driver, EndpointAllocError};
4use super::types::*; 6use super::types::*;
5use super::UsbDevice; 7use super::UsbDevice;
8use super::MAX_CLASS_COUNT;
6 9
7#[derive(Debug, Copy, Clone)] 10#[derive(Debug, Copy, Clone)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))] 11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -116,6 +119,7 @@ impl<'a> Config<'a> {
116/// Used to build new [`UsbDevice`]s. 119/// Used to build new [`UsbDevice`]s.
117pub struct UsbDeviceBuilder<'d, D: Driver<'d>> { 120pub struct UsbDeviceBuilder<'d, D: Driver<'d>> {
118 config: Config<'d>, 121 config: Config<'d>,
122 classes: Vec<&'d mut dyn UsbClass, MAX_CLASS_COUNT>,
119 123
120 bus: D, 124 bus: D,
121 next_interface_number: u8, 125 next_interface_number: u8,
@@ -165,6 +169,7 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
165 UsbDeviceBuilder { 169 UsbDeviceBuilder {
166 bus, 170 bus,
167 config, 171 config,
172 classes: Vec::new(),
168 next_interface_number: 0, 173 next_interface_number: 0,
169 next_string_index: 4, 174 next_string_index: 4,
170 175
@@ -175,7 +180,7 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
175 } 180 }
176 181
177 /// Creates the [`UsbDevice`] instance with the configuration in this builder. 182 /// Creates the [`UsbDevice`] instance with the configuration in this builder.
178 pub fn build(mut self, classes: &'d mut [&'d mut dyn UsbClass]) -> UsbDevice<'d, D> { 183 pub fn build(mut self) -> UsbDevice<'d, D> {
179 self.config_descriptor.end_configuration(); 184 self.config_descriptor.end_configuration();
180 self.bos_descriptor.end_bos(); 185 self.bos_descriptor.end_bos();
181 186
@@ -185,10 +190,16 @@ impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
185 self.device_descriptor.into_buf(), 190 self.device_descriptor.into_buf(),
186 self.config_descriptor.into_buf(), 191 self.config_descriptor.into_buf(),
187 self.bos_descriptor.writer.into_buf(), 192 self.bos_descriptor.writer.into_buf(),
188 classes, 193 self.classes,
189 ) 194 )
190 } 195 }
191 196
197 pub fn add_class(&mut self, class: &'d mut dyn UsbClass) {
198 if self.classes.push(class).is_err() {
199 panic!("max class count reached")
200 }
201 }
202
192 /// Allocates a new interface number. 203 /// Allocates a new interface number.
193 pub fn alloc_interface(&mut self) -> InterfaceNumber { 204 pub fn alloc_interface(&mut self) -> InterfaceNumber {
194 let number = self.next_interface_number; 205 let number = self.next_interface_number;
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index ff3930afa..2c00e8817 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -14,6 +14,7 @@ pub mod types;
14mod util; 14mod util;
15 15
16use class::ControlInRequestStatus; 16use class::ControlInRequestStatus;
17use heapless::Vec;
17 18
18use self::class::{RequestStatus, UsbClass}; 19use self::class::{RequestStatus, UsbClass};
19use self::control::*; 20use self::control::*;
@@ -53,6 +54,8 @@ pub const CONFIGURATION_VALUE: u8 = 1;
53/// The default value for bAlternateSetting for all interfaces. 54/// The default value for bAlternateSetting for all interfaces.
54pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; 55pub const DEFAULT_ALTERNATE_SETTING: u8 = 0;
55 56
57pub const MAX_CLASS_COUNT: usize = 4;
58
56pub struct UsbDevice<'d, D: Driver<'d>> { 59pub struct UsbDevice<'d, D: Driver<'d>> {
57 bus: D::Bus, 60 bus: D::Bus,
58 control: D::ControlPipe, 61 control: D::ControlPipe,
@@ -67,7 +70,7 @@ pub struct UsbDevice<'d, D: Driver<'d>> {
67 self_powered: bool, 70 self_powered: bool,
68 pending_address: u8, 71 pending_address: u8,
69 72
70 classes: &'d mut [&'d mut dyn UsbClass], 73 classes: Vec<&'d mut dyn UsbClass, MAX_CLASS_COUNT>,
71} 74}
72 75
73impl<'d, D: Driver<'d>> UsbDevice<'d, D> { 76impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
@@ -77,7 +80,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
77 device_descriptor: &'d [u8], 80 device_descriptor: &'d [u8],
78 config_descriptor: &'d [u8], 81 config_descriptor: &'d [u8],
79 bos_descriptor: &'d [u8], 82 bos_descriptor: &'d [u8],
80 classes: &'d mut [&'d mut dyn UsbClass], 83 classes: Vec<&'d mut dyn UsbClass, MAX_CLASS_COUNT>,
81 ) -> Self { 84 ) -> Self {
82 let control = driver 85 let control = driver
83 .alloc_control_pipe(config.max_packet_size_0 as u16) 86 .alloc_control_pipe(config.max_packet_size_0 as u16)
diff --git a/examples/nrf/src/bin/usb/cdc_acm.rs b/examples/nrf/src/bin/usb/cdc_acm.rs
index 5e4abfea2..92cc16eb4 100644
--- a/examples/nrf/src/bin/usb/cdc_acm.rs
+++ b/examples/nrf/src/bin/usb/cdc_acm.rs
@@ -1,5 +1,8 @@
1use core::mem; 1use core::cell::{Cell, UnsafeCell};
2use core::mem::{self, MaybeUninit};
3use core::sync::atomic::{AtomicBool, Ordering};
2use defmt::info; 4use defmt::info;
5use embassy::blocking_mutex::CriticalSectionMutex;
3use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass}; 6use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass};
4use embassy_usb::control::{self, Request}; 7use embassy_usb::control::{self, Request};
5use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError}; 8use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError};
@@ -25,6 +28,18 @@ const REQ_SET_LINE_CODING: u8 = 0x20;
25const REQ_GET_LINE_CODING: u8 = 0x21; 28const REQ_GET_LINE_CODING: u8 = 0x21;
26const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; 29const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
27 30
31pub struct State {
32 control: MaybeUninit<Control>,
33}
34
35impl State {
36 pub fn new() -> Self {
37 Self {
38 control: MaybeUninit::uninit(),
39 }
40 }
41}
42
28/// Packet level implementation of a CDC-ACM serial port. 43/// Packet level implementation of a CDC-ACM serial port.
29/// 44///
30/// This class can be used directly and it has the least overhead due to directly reading and 45/// This class can be used directly and it has the least overhead due to directly reading and
@@ -45,21 +60,32 @@ pub struct CdcAcmClass<'d, D: Driver<'d>> {
45 pub data_if: InterfaceNumber, 60 pub data_if: InterfaceNumber,
46 pub read_ep: D::EndpointOut, 61 pub read_ep: D::EndpointOut,
47 pub write_ep: D::EndpointIn, 62 pub write_ep: D::EndpointIn,
48 pub control: CdcAcmControl, 63 control: &'d ControlShared,
64}
65
66struct Control {
67 comm_if: InterfaceNumber,
68 shared: UnsafeCell<ControlShared>,
49} 69}
50 70
51pub struct CdcAcmControl { 71/// Shared data between Control and CdcAcmClass
52 pub comm_if: InterfaceNumber, 72struct ControlShared {
53 pub line_coding: LineCoding, 73 line_coding: CriticalSectionMutex<Cell<LineCoding>>,
54 pub dtr: bool, 74 dtr: AtomicBool,
55 pub rts: bool, 75 rts: AtomicBool,
56} 76}
57 77
58impl UsbClass for CdcAcmControl { 78impl Control {
79 fn shared(&mut self) -> &ControlShared {
80 unsafe { &*(self.shared.get() as *const _) }
81 }
82}
83impl UsbClass for Control {
59 fn reset(&mut self) { 84 fn reset(&mut self) {
60 self.line_coding = LineCoding::default(); 85 let shared = self.shared();
61 self.dtr = false; 86 shared.line_coding.lock(|x| x.set(LineCoding::default()));
62 self.rts = false; 87 shared.dtr.store(false, Ordering::Relaxed);
88 shared.rts.store(false, Ordering::Relaxed);
63 } 89 }
64 90
65 fn control_out(&mut self, req: control::Request, data: &[u8]) -> RequestStatus { 91 fn control_out(&mut self, req: control::Request, data: &[u8]) -> RequestStatus {
@@ -77,18 +103,25 @@ impl UsbClass for CdcAcmControl {
77 RequestStatus::Accepted 103 RequestStatus::Accepted
78 } 104 }
79 REQ_SET_LINE_CODING if data.len() >= 7 => { 105 REQ_SET_LINE_CODING if data.len() >= 7 => {
80 self.line_coding.data_rate = u32::from_le_bytes(data[0..4].try_into().unwrap()); 106 let coding = LineCoding {
81 self.line_coding.stop_bits = data[4].into(); 107 data_rate: u32::from_le_bytes(data[0..4].try_into().unwrap()),
82 self.line_coding.parity_type = data[5].into(); 108 stop_bits: data[4].into(),
83 self.line_coding.data_bits = data[6]; 109 parity_type: data[5].into(),
84 info!("Set line coding to: {:?}", self.line_coding); 110 data_bits: data[6],
111 };
112 self.shared().line_coding.lock(|x| x.set(coding));
113 info!("Set line coding to: {:?}", coding);
85 114
86 RequestStatus::Accepted 115 RequestStatus::Accepted
87 } 116 }
88 REQ_SET_CONTROL_LINE_STATE => { 117 REQ_SET_CONTROL_LINE_STATE => {
89 self.dtr = (req.value & 0x0001) != 0; 118 let dtr = (req.value & 0x0001) != 0;
90 self.rts = (req.value & 0x0002) != 0; 119 let rts = (req.value & 0x0002) != 0;
91 info!("Set dtr {}, rts {}", self.dtr, self.rts); 120
121 let shared = self.shared();
122 shared.dtr.store(dtr, Ordering::Relaxed);
123 shared.rts.store(rts, Ordering::Relaxed);
124 info!("Set dtr {}, rts {}", dtr, rts);
92 125
93 RequestStatus::Accepted 126 RequestStatus::Accepted
94 } 127 }
@@ -112,11 +145,12 @@ impl UsbClass for CdcAcmControl {
112 // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. 145 // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
113 REQ_GET_LINE_CODING if req.length == 7 => { 146 REQ_GET_LINE_CODING if req.length == 7 => {
114 info!("Sending line coding"); 147 info!("Sending line coding");
148 let coding = self.shared().line_coding.lock(|x| x.get());
115 let mut data = [0; 7]; 149 let mut data = [0; 7];
116 data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); 150 data[0..4].copy_from_slice(&coding.data_rate.to_le_bytes());
117 data[4] = self.line_coding.stop_bits as u8; 151 data[4] = coding.stop_bits as u8;
118 data[5] = self.line_coding.parity_type as u8; 152 data[5] = coding.parity_type as u8;
119 data[6] = self.line_coding.data_bits; 153 data[6] = coding.data_bits;
120 control.accept(&data) 154 control.accept(&data)
121 } 155 }
122 _ => control.reject(), 156 _ => control.reject(),
@@ -127,7 +161,11 @@ impl UsbClass for CdcAcmControl {
127impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { 161impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
128 /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For 162 /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
129 /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. 163 /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
130 pub fn new(builder: &mut UsbDeviceBuilder<'d, D>, max_packet_size: u16) -> Self { 164 pub fn new(
165 builder: &mut UsbDeviceBuilder<'d, D>,
166 state: &'d mut State,
167 max_packet_size: u16,
168 ) -> Self {
131 let comm_if = builder.alloc_interface(); 169 let comm_if = builder.alloc_interface();
132 let comm_ep = builder.alloc_interrupt_endpoint_in(8, 255); 170 let comm_ep = builder.alloc_interrupt_endpoint_in(8, 255);
133 let data_if = builder.alloc_interface(); 171 let data_if = builder.alloc_interface();
@@ -207,22 +245,29 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
207 builder.config_descriptor.endpoint(write_ep.info()).unwrap(); 245 builder.config_descriptor.endpoint(write_ep.info()).unwrap();
208 builder.config_descriptor.endpoint(read_ep.info()).unwrap(); 246 builder.config_descriptor.endpoint(read_ep.info()).unwrap();
209 247
248 let control = state.control.write(Control {
249 comm_if,
250 shared: UnsafeCell::new(ControlShared {
251 dtr: AtomicBool::new(false),
252 rts: AtomicBool::new(false),
253 line_coding: CriticalSectionMutex::new(Cell::new(LineCoding {
254 stop_bits: StopBits::One,
255 data_bits: 8,
256 parity_type: ParityType::None,
257 data_rate: 8_000,
258 })),
259 }),
260 });
261
262 let control_shared = unsafe { &*(control.shared.get() as *const _) };
263 builder.add_class(control);
264
210 CdcAcmClass { 265 CdcAcmClass {
211 comm_ep, 266 comm_ep,
212 data_if, 267 data_if,
213 read_ep, 268 read_ep,
214 write_ep, 269 write_ep,
215 control: CdcAcmControl { 270 control: control_shared,
216 comm_if,
217 dtr: false,
218 rts: false,
219 line_coding: LineCoding {
220 stop_bits: StopBits::One,
221 data_bits: 8,
222 parity_type: ParityType::None,
223 data_rate: 8_000,
224 },
225 },
226 } 271 }
227 } 272 }
228 273
@@ -234,18 +279,18 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
234 279
235 /// Gets the current line coding. The line coding contains information that's mainly relevant 280 /// Gets the current line coding. The line coding contains information that's mainly relevant
236 /// for USB to UART serial port emulators, and can be ignored if not relevant. 281 /// for USB to UART serial port emulators, and can be ignored if not relevant.
237 pub fn line_coding(&self) -> &LineCoding { 282 pub fn line_coding(&self) -> LineCoding {
238 &self.control.line_coding 283 self.control.line_coding.lock(|x| x.get())
239 } 284 }
240 285
241 /// Gets the DTR (data terminal ready) state 286 /// Gets the DTR (data terminal ready) state
242 pub fn dtr(&self) -> bool { 287 pub fn dtr(&self) -> bool {
243 self.control.dtr 288 self.control.dtr.load(Ordering::Relaxed)
244 } 289 }
245 290
246 /// Gets the RTS (request to send) state 291 /// Gets the RTS (request to send) state
247 pub fn rts(&self) -> bool { 292 pub fn rts(&self) -> bool {
248 self.control.rts 293 self.control.rts.load(Ordering::Relaxed)
249 } 294 }
250 295
251 /// Writes a single packet into the IN endpoint. 296 /// Writes a single packet into the IN endpoint.
@@ -264,88 +309,6 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
264 } 309 }
265} 310}
266 311
267/*
268impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> {
269 fn get_configuration_descriptors(&self, builder.config_descriptor: &mut Descriptorbuilder.config_descriptor) -> Result<()> {
270
271 Ok(())
272 }
273
274 fn reset(&mut self) {
275 self.line_coding = LineCoding::default();
276 self.dtr = false;
277 self.rts = false;
278 }
279
280 fn control_in(&mut self, xfer: ControlIn<B>) {
281 let req = xfer.request();
282
283 if !(req.request_type == control::RequestType::Class
284 && req.recipient == control::Recipient::Interface
285 && req.index == u8::from(self.comm_if) as u16)
286 {
287 return;
288 }
289
290 match req.request {
291 // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
292 REQ_GET_LINE_CODING if req.length == 7 => {
293 xfer.accept(|data| {
294 data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
295 data[4] = self.line_coding.stop_bits as u8;
296 data[5] = self.line_coding.parity_type as u8;
297 data[6] = self.line_coding.data_bits;
298
299 Ok(7)
300 })
301 .ok();
302 }
303 _ => {
304 xfer.reject().ok();
305 }
306 }
307 }
308
309 fn control_out(&mut self, xfer: ControlOut<B>) {
310 let req = xfer.request();
311
312 if !(req.request_type == control::RequestType::Class
313 && req.recipient == control::Recipient::Interface
314 && req.index == u8::from(self.comm_if) as u16)
315 {
316 return;
317 }
318
319 match req.request {
320 REQ_SEND_ENCAPSULATED_COMMAND => {
321 // We don't actually support encapsulated commands but pretend we do for standards
322 // compatibility.
323 xfer.accept().ok();
324 }
325 REQ_SET_LINE_CODING if xfer.data().len() >= 7 => {
326 self.line_coding.data_rate =
327 u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap());
328 self.line_coding.stop_bits = xfer.data()[4].into();
329 self.line_coding.parity_type = xfer.data()[5].into();
330 self.line_coding.data_bits = xfer.data()[6];
331
332 xfer.accept().ok();
333 }
334 REQ_SET_CONTROL_LINE_STATE => {
335 self.dtr = (req.value & 0x0001) != 0;
336 self.rts = (req.value & 0x0002) != 0;
337
338 xfer.accept().ok();
339 }
340 _ => {
341 xfer.reject().ok();
342 }
343 };
344 }
345}
346
347 */
348
349/// Number of stop bits for LineCoding 312/// Number of stop bits for LineCoding
350#[derive(Copy, Clone, PartialEq, Eq, defmt::Format)] 313#[derive(Copy, Clone, PartialEq, Eq, defmt::Format)]
351pub enum StopBits { 314pub enum StopBits {
@@ -393,7 +356,7 @@ impl From<u8> for ParityType {
393/// 356///
394/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can 357/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
395/// be ignored if you don't plan to interface with a physical UART. 358/// be ignored if you don't plan to interface with a physical UART.
396#[derive(defmt::Format)] 359#[derive(Clone, Copy, defmt::Format)]
397pub struct LineCoding { 360pub struct LineCoding {
398 stop_bits: StopBits, 361 stop_bits: StopBits,
399 data_bits: u8, 362 data_bits: u8,
diff --git a/examples/nrf/src/bin/usb/main.rs b/examples/nrf/src/bin/usb/main.rs
index 73ac3a21f..398dd07b6 100644
--- a/examples/nrf/src/bin/usb/main.rs
+++ b/examples/nrf/src/bin/usb/main.rs
@@ -16,12 +16,11 @@ 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;
20use embassy_usb::driver::{EndpointIn, EndpointOut}; 19use embassy_usb::driver::{EndpointIn, EndpointOut};
21use embassy_usb::{Config, UsbDeviceBuilder}; 20use embassy_usb::{Config, UsbDeviceBuilder};
22use futures::future::join3; 21use futures::future::join3;
23 22
24use crate::cdc_acm::CdcAcmClass; 23use crate::cdc_acm::{CdcAcmClass, State};
25 24
26#[embassy::main] 25#[embassy::main]
27async fn main(_spawner: Spawner, p: Peripherals) { 26async fn main(_spawner: Spawner, p: Peripherals) {
@@ -48,6 +47,9 @@ async fn main(_spawner: Spawner, p: Peripherals) {
48 let mut device_descriptor = [0; 256]; 47 let mut device_descriptor = [0; 256];
49 let mut config_descriptor = [0; 256]; 48 let mut config_descriptor = [0; 256];
50 let mut bos_descriptor = [0; 256]; 49 let mut bos_descriptor = [0; 256];
50
51 let mut state = State::new();
52
51 let mut builder = UsbDeviceBuilder::new( 53 let mut builder = UsbDeviceBuilder::new(
52 driver, 54 driver,
53 config, 55 config,
@@ -57,11 +59,10 @@ async fn main(_spawner: Spawner, p: Peripherals) {
57 ); 59 );
58 60
59 // Create classes on the builder. 61 // Create classes on the builder.
60 let mut class = CdcAcmClass::new(&mut builder, 64); 62 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
61 63
62 // Build the builder. 64 // Build the builder.
63 let mut classes: [&mut dyn UsbClass; 1] = [&mut class.control]; 65 let mut usb = builder.build();
64 let mut usb = builder.build(&mut classes);
65 66
66 // Run the USB device. 67 // Run the USB device.
67 let fut1 = usb.run(); 68 let fut1 = usb.run();