diff options
Diffstat (limited to 'examples/nrf/src')
| -rw-r--r-- | examples/nrf/src/bin/usb/cdc_acm.rs | 127 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb/main.rs | 3 |
2 files changed, 114 insertions, 16 deletions
diff --git a/examples/nrf/src/bin/usb/cdc_acm.rs b/examples/nrf/src/bin/usb/cdc_acm.rs index b7c112ae6..eebf89221 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::convert::TryInto; | 1 | use core::future::Future; |
| 2 | use core::mem; | 2 | use core::mem; |
| 3 | use defmt::info; | ||
| 4 | use embassy_usb::class::{ControlInRequestStatus, RequestStatus, UsbClass}; | ||
| 5 | use embassy_usb::control::{self, Request}; | ||
| 3 | use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError}; | 6 | use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError}; |
| 4 | use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder}; | 7 | use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder}; |
| 5 | 8 | ||
| @@ -39,16 +42,107 @@ const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | |||
| 39 | /// terminated with a short packet, even if the bulk endpoint is used for stream-like data. | 42 | /// terminated with a short packet, even if the bulk endpoint is used for stream-like data. |
| 40 | pub struct CdcAcmClass<'d, D: Driver<'d>> { | 43 | pub struct CdcAcmClass<'d, D: Driver<'d>> { |
| 41 | // TODO not pub | 44 | // TODO not pub |
| 42 | pub comm_if: InterfaceNumber, | ||
| 43 | pub comm_ep: D::EndpointIn, | 45 | pub comm_ep: D::EndpointIn, |
| 44 | pub data_if: InterfaceNumber, | 46 | pub data_if: InterfaceNumber, |
| 45 | pub read_ep: D::EndpointOut, | 47 | pub read_ep: D::EndpointOut, |
| 46 | pub write_ep: D::EndpointIn, | 48 | pub write_ep: D::EndpointIn, |
| 49 | pub control: CdcAcmControl, | ||
| 50 | } | ||
| 51 | |||
| 52 | pub struct CdcAcmControl { | ||
| 53 | pub comm_if: InterfaceNumber, | ||
| 47 | pub line_coding: LineCoding, | 54 | pub line_coding: LineCoding, |
| 48 | pub dtr: bool, | 55 | pub dtr: bool, |
| 49 | pub rts: bool, | 56 | pub rts: bool, |
| 50 | } | 57 | } |
| 51 | 58 | ||
| 59 | impl<'d, D: Driver<'d>> UsbClass<'d, D> 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) { | ||
| 64 | self.line_coding = LineCoding::default(); | ||
| 65 | self.dtr = false; | ||
| 66 | self.rts = false; | ||
| 67 | } | ||
| 68 | |||
| 69 | fn control_out<'a>( | ||
| 70 | &'a mut self, | ||
| 71 | req: control::Request, | ||
| 72 | data: &'a [u8], | ||
| 73 | ) -> Self::ControlOutFuture<'a> | ||
| 74 | where | ||
| 75 | 'd: 'a, | ||
| 76 | D: 'a, | ||
| 77 | { | ||
| 78 | async move { | ||
| 79 | if !(req.request_type == control::RequestType::Class | ||
| 80 | && req.recipient == control::Recipient::Interface | ||
| 81 | && req.index == u8::from(self.comm_if) as u16) | ||
| 82 | { | ||
| 83 | return RequestStatus::Unhandled; | ||
| 84 | } | ||
| 85 | |||
| 86 | match req.request { | ||
| 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 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | fn control_in<'a>( | ||
| 114 | &'a mut self, | ||
| 115 | req: Request, | ||
| 116 | control: embassy_usb::class::ControlIn<'a, 'd, D>, | ||
| 117 | ) -> Self::ControlInFuture<'a> | ||
| 118 | where | ||
| 119 | 'd: 'a, | ||
| 120 | { | ||
| 121 | async move { | ||
| 122 | if !(req.request_type == control::RequestType::Class | ||
| 123 | && req.recipient == control::Recipient::Interface | ||
| 124 | && req.index == u8::from(self.comm_if) as u16) | ||
| 125 | { | ||
| 126 | return control.ignore(); | ||
| 127 | } | ||
| 128 | |||
| 129 | match req.request { | ||
| 130 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. | ||
| 131 | REQ_GET_LINE_CODING if req.length == 7 => { | ||
| 132 | info!("Sending line coding"); | ||
| 133 | let mut data = [0; 7]; | ||
| 134 | data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); | ||
| 135 | data[4] = self.line_coding.stop_bits as u8; | ||
| 136 | data[5] = self.line_coding.parity_type as u8; | ||
| 137 | data[6] = self.line_coding.data_bits; | ||
| 138 | control.accept(&data).await | ||
| 139 | } | ||
| 140 | _ => control.reject(), | ||
| 141 | } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 52 | impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | 146 | impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { |
| 53 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For | 147 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For |
| 54 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. | 148 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. |
| @@ -133,19 +227,21 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | |||
| 133 | builder.config_descriptor.endpoint(read_ep.info()).unwrap(); | 227 | builder.config_descriptor.endpoint(read_ep.info()).unwrap(); |
| 134 | 228 | ||
| 135 | CdcAcmClass { | 229 | CdcAcmClass { |
| 136 | comm_if, | ||
| 137 | comm_ep, | 230 | comm_ep, |
| 138 | data_if, | 231 | data_if, |
| 139 | read_ep, | 232 | read_ep, |
| 140 | write_ep, | 233 | write_ep, |
| 141 | line_coding: LineCoding { | 234 | control: CdcAcmControl { |
| 142 | stop_bits: StopBits::One, | 235 | comm_if, |
| 143 | data_bits: 8, | 236 | dtr: false, |
| 144 | parity_type: ParityType::None, | 237 | rts: false, |
| 145 | data_rate: 8_000, | 238 | line_coding: LineCoding { |
| 239 | stop_bits: StopBits::One, | ||
| 240 | data_bits: 8, | ||
| 241 | parity_type: ParityType::None, | ||
| 242 | data_rate: 8_000, | ||
| 243 | }, | ||
| 146 | }, | 244 | }, |
| 147 | dtr: false, | ||
| 148 | rts: false, | ||
| 149 | } | 245 | } |
| 150 | } | 246 | } |
| 151 | 247 | ||
| @@ -158,17 +254,17 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | |||
| 158 | /// Gets the current line coding. The line coding contains information that's mainly relevant | 254 | /// Gets the current line coding. The line coding contains information that's mainly relevant |
| 159 | /// for USB to UART serial port emulators, and can be ignored if not relevant. | 255 | /// for USB to UART serial port emulators, and can be ignored if not relevant. |
| 160 | pub fn line_coding(&self) -> &LineCoding { | 256 | pub fn line_coding(&self) -> &LineCoding { |
| 161 | &self.line_coding | 257 | &self.control.line_coding |
| 162 | } | 258 | } |
| 163 | 259 | ||
| 164 | /// Gets the DTR (data terminal ready) state | 260 | /// Gets the DTR (data terminal ready) state |
| 165 | pub fn dtr(&self) -> bool { | 261 | pub fn dtr(&self) -> bool { |
| 166 | self.dtr | 262 | self.control.dtr |
| 167 | } | 263 | } |
| 168 | 264 | ||
| 169 | /// Gets the RTS (request to send) state | 265 | /// Gets the RTS (request to send) state |
| 170 | pub fn rts(&self) -> bool { | 266 | pub fn rts(&self) -> bool { |
| 171 | self.rts | 267 | self.control.rts |
| 172 | } | 268 | } |
| 173 | 269 | ||
| 174 | /// Writes a single packet into the IN endpoint. | 270 | /// Writes a single packet into the IN endpoint. |
| @@ -270,7 +366,7 @@ impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> { | |||
| 270 | */ | 366 | */ |
| 271 | 367 | ||
| 272 | /// Number of stop bits for LineCoding | 368 | /// Number of stop bits for LineCoding |
| 273 | #[derive(Copy, Clone, PartialEq, Eq)] | 369 | #[derive(Copy, Clone, PartialEq, Eq, defmt::Format)] |
| 274 | pub enum StopBits { | 370 | pub enum StopBits { |
| 275 | /// 1 stop bit | 371 | /// 1 stop bit |
| 276 | One = 0, | 372 | One = 0, |
| @@ -293,7 +389,7 @@ impl From<u8> for StopBits { | |||
| 293 | } | 389 | } |
| 294 | 390 | ||
| 295 | /// Parity for LineCoding | 391 | /// Parity for LineCoding |
| 296 | #[derive(Copy, Clone, PartialEq, Eq)] | 392 | #[derive(Copy, Clone, PartialEq, Eq, defmt::Format)] |
| 297 | pub enum ParityType { | 393 | pub enum ParityType { |
| 298 | None = 0, | 394 | None = 0, |
| 299 | Odd = 1, | 395 | Odd = 1, |
| @@ -316,6 +412,7 @@ impl From<u8> for ParityType { | |||
| 316 | /// | 412 | /// |
| 317 | /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can | 413 | /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can |
| 318 | /// be ignored if you don't plan to interface with a physical UART. | 414 | /// be ignored if you don't plan to interface with a physical UART. |
| 415 | #[derive(defmt::Format)] | ||
| 319 | pub struct LineCoding { | 416 | pub struct LineCoding { |
| 320 | stop_bits: StopBits, | 417 | stop_bits: StopBits, |
| 321 | data_bits: u8, | 418 | data_bits: u8, |
diff --git a/examples/nrf/src/bin/usb/main.rs b/examples/nrf/src/bin/usb/main.rs index ecbdc3461..71285579c 100644 --- a/examples/nrf/src/bin/usb/main.rs +++ b/examples/nrf/src/bin/usb/main.rs | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(generic_associated_types)] | ||
| 3 | #![feature(type_alias_impl_trait)] | 4 | #![feature(type_alias_impl_trait)] |
| 4 | 5 | ||
| 5 | #[path = "../../example_common.rs"] | 6 | #[path = "../../example_common.rs"] |
| @@ -58,7 +59,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 58 | let mut class = CdcAcmClass::new(&mut builder, 64); | 59 | let mut class = CdcAcmClass::new(&mut builder, 64); |
| 59 | 60 | ||
| 60 | // Build the builder. | 61 | // Build the builder. |
| 61 | let mut usb = builder.build(); | 62 | let mut usb = builder.build(class.control); |
| 62 | 63 | ||
| 63 | // Run the USB device. | 64 | // Run the USB device. |
| 64 | let fut1 = usb.run(); | 65 | let fut1 = usb.run(); |
