aboutsummaryrefslogtreecommitdiff
path: root/examples/nrf
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 /examples/nrf
parenta062baae3837b453f0c05d0730ba465c6d2c2446 (diff)
usb: add `add_class` to builder, so that `FooBarClass::new(&mut builder)` can set up everything.
Diffstat (limited to 'examples/nrf')
-rw-r--r--examples/nrf/src/bin/usb/cdc_acm.rs207
-rw-r--r--examples/nrf/src/bin/usb/main.rs11
2 files changed, 91 insertions, 127 deletions
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();