diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-03-28 02:20:01 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-04-06 05:38:11 +0200 |
| commit | a2f5763a67441e5e6c981407938c21c58e9eb669 (patch) | |
| tree | 48f5d58eaf04e6a85da706e6960ac96cf5cd02ab | |
| parent | a062baae3837b453f0c05d0730ba465c6d2c2446 (diff) | |
usb: add `add_class` to builder, so that `FooBarClass::new(&mut builder)` can set up everything.
| -rw-r--r-- | embassy-usb/Cargo.toml | 1 | ||||
| -rw-r--r-- | embassy-usb/src/builder.rs | 15 | ||||
| -rw-r--r-- | embassy-usb/src/lib.rs | 7 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb/cdc_acm.rs | 207 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb/main.rs | 11 |
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 } | |||
| 10 | log = { version = "0.4.14", optional = true } | 10 | log = { version = "0.4.14", optional = true } |
| 11 | cortex-m = "0.7.3" | 11 | cortex-m = "0.7.3" |
| 12 | num-traits = { version = "0.2.14", default-features = false } | 12 | num-traits = { version = "0.2.14", default-features = false } |
| 13 | heapless = "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 @@ | |||
| 1 | use heapless::Vec; | ||
| 2 | |||
| 1 | use super::class::UsbClass; | 3 | use super::class::UsbClass; |
| 2 | use super::descriptor::{BosWriter, DescriptorWriter}; | 4 | use super::descriptor::{BosWriter, DescriptorWriter}; |
| 3 | use super::driver::{Driver, EndpointAllocError}; | 5 | use super::driver::{Driver, EndpointAllocError}; |
| 4 | use super::types::*; | 6 | use super::types::*; |
| 5 | use super::UsbDevice; | 7 | use super::UsbDevice; |
| 8 | use 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. |
| 117 | pub struct UsbDeviceBuilder<'d, D: Driver<'d>> { | 120 | pub 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; | |||
| 14 | mod util; | 14 | mod util; |
| 15 | 15 | ||
| 16 | use class::ControlInRequestStatus; | 16 | use class::ControlInRequestStatus; |
| 17 | use heapless::Vec; | ||
| 17 | 18 | ||
| 18 | use self::class::{RequestStatus, UsbClass}; | 19 | use self::class::{RequestStatus, UsbClass}; |
| 19 | use self::control::*; | 20 | use 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. |
| 54 | pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; | 55 | pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; |
| 55 | 56 | ||
| 57 | pub const MAX_CLASS_COUNT: usize = 4; | ||
| 58 | |||
| 56 | pub struct UsbDevice<'d, D: Driver<'d>> { | 59 | pub 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 | ||
| 73 | impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | 76 | impl<'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 @@ | |||
| 1 | use core::mem; | 1 | use core::cell::{Cell, UnsafeCell}; |
| 2 | use core::mem::{self, MaybeUninit}; | ||
| 3 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 2 | use defmt::info; | 4 | use defmt::info; |
| 5 | use embassy::blocking_mutex::CriticalSectionMutex; | ||
| 3 | use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass}; | 6 | use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass}; |
| 4 | use embassy_usb::control::{self, Request}; | 7 | use embassy_usb::control::{self, Request}; |
| 5 | use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError}; | 8 | use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError}; |
| @@ -25,6 +28,18 @@ const REQ_SET_LINE_CODING: u8 = 0x20; | |||
| 25 | const REQ_GET_LINE_CODING: u8 = 0x21; | 28 | const REQ_GET_LINE_CODING: u8 = 0x21; |
| 26 | const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | 29 | const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; |
| 27 | 30 | ||
| 31 | pub struct State { | ||
| 32 | control: MaybeUninit<Control>, | ||
| 33 | } | ||
| 34 | |||
| 35 | impl 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 | |||
| 66 | struct Control { | ||
| 67 | comm_if: InterfaceNumber, | ||
| 68 | shared: UnsafeCell<ControlShared>, | ||
| 49 | } | 69 | } |
| 50 | 70 | ||
| 51 | pub struct CdcAcmControl { | 71 | /// Shared data between Control and CdcAcmClass |
| 52 | pub comm_if: InterfaceNumber, | 72 | struct 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 | ||
| 58 | impl UsbClass for CdcAcmControl { | 78 | impl Control { |
| 79 | fn shared(&mut self) -> &ControlShared { | ||
| 80 | unsafe { &*(self.shared.get() as *const _) } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | impl 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 { | |||
| 127 | impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | 161 | impl<'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 | /* | ||
| 268 | impl<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)] |
| 351 | pub enum StopBits { | 314 | pub 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)] |
| 397 | pub struct LineCoding { | 360 | pub 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; | |||
| 16 | use embassy_nrf::pac; | 16 | use embassy_nrf::pac; |
| 17 | use embassy_nrf::usb::Driver; | 17 | use embassy_nrf::usb::Driver; |
| 18 | use embassy_nrf::Peripherals; | 18 | use embassy_nrf::Peripherals; |
| 19 | use embassy_usb::class::UsbClass; | ||
| 20 | use embassy_usb::driver::{EndpointIn, EndpointOut}; | 19 | use embassy_usb::driver::{EndpointIn, EndpointOut}; |
| 21 | use embassy_usb::{Config, UsbDeviceBuilder}; | 20 | use embassy_usb::{Config, UsbDeviceBuilder}; |
| 22 | use futures::future::join3; | 21 | use futures::future::join3; |
| 23 | 22 | ||
| 24 | use crate::cdc_acm::CdcAcmClass; | 23 | use crate::cdc_acm::{CdcAcmClass, State}; |
| 25 | 24 | ||
| 26 | #[embassy::main] | 25 | #[embassy::main] |
| 27 | async fn main(_spawner: Spawner, p: Peripherals) { | 26 | async 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(); |
