diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-05-04 01:00:38 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-05-04 01:41:37 +0200 |
| commit | fc32b3750c448a81b7dd44cf9de98723b8eb4fcf (patch) | |
| tree | 989562f5750d28ab11732633839db8f25a1ad773 | |
| parent | 85c0525e01c52bbb85c7b93600a60837ee7b87dc (diff) | |
Remove embassy_hal_common::usb.
The replacement is `embassy-usb`. There's a WIP driver for stm32 USBD in #709,
there's no WIP driver for stm32 USB_OTG. This means we're left without
USB_OTG support for now.
Reason for removing is I'm going to soon remove `embassy::io`, and
USB uses it. I don't want to spend time maintaining "dead" code
that is going to be removed. Volunteers welcome, either to update
old USB to the new IO, or write a USB_OTG driver fo the new USB.
| -rw-r--r-- | embassy-hal-common/Cargo.toml | 1 | ||||
| -rw-r--r-- | embassy-hal-common/src/lib.rs | 1 | ||||
| -rw-r--r-- | embassy-hal-common/src/usb/cdc_acm.rs | 338 | ||||
| -rw-r--r-- | embassy-hal-common/src/usb/mod.rs | 267 | ||||
| -rw-r--r-- | embassy-hal-common/src/usb/usb_serial.rs | 345 | ||||
| -rw-r--r-- | embassy-stm32/Cargo.toml | 2 | ||||
| -rw-r--r-- | embassy-stm32/build.rs | 32 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/usb_otg.rs | 61 | ||||
| -rw-r--r-- | examples/stm32f4/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/usb_uart.rs | 99 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/usb_uart_ulpi.rs | 114 | ||||
| -rw-r--r-- | examples/stm32l4/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/stm32l4/src/bin/usb_uart.rs | 115 |
14 files changed, 39 insertions, 1342 deletions
diff --git a/embassy-hal-common/Cargo.toml b/embassy-hal-common/Cargo.toml index 2028b0e0c..fee6da6ff 100644 --- a/embassy-hal-common/Cargo.toml +++ b/embassy-hal-common/Cargo.toml | |||
| @@ -12,5 +12,4 @@ embassy = { version = "0.1.0", path = "../embassy" } | |||
| 12 | defmt = { version = "0.3", optional = true } | 12 | defmt = { version = "0.3", optional = true } |
| 13 | log = { version = "0.4.14", optional = true } | 13 | log = { version = "0.4.14", optional = true } |
| 14 | cortex-m = "0.7.3" | 14 | cortex-m = "0.7.3" |
| 15 | usb-device = "0.2.8" | ||
| 16 | num-traits = { version = "0.2.14", default-features = false } | 15 | num-traits = { version = "0.2.14", default-features = false } |
diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index 1af30c6b4..6ee2ccd59 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs | |||
| @@ -10,7 +10,6 @@ mod macros; | |||
| 10 | pub mod peripheral; | 10 | pub mod peripheral; |
| 11 | pub mod ratio; | 11 | pub mod ratio; |
| 12 | pub mod ring_buffer; | 12 | pub mod ring_buffer; |
| 13 | pub mod usb; | ||
| 14 | 13 | ||
| 15 | /// Low power blocking wait loop using WFE/SEV. | 14 | /// Low power blocking wait loop using WFE/SEV. |
| 16 | pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) { | 15 | pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) { |
diff --git a/embassy-hal-common/src/usb/cdc_acm.rs b/embassy-hal-common/src/usb/cdc_acm.rs deleted file mode 100644 index 5a85b3846..000000000 --- a/embassy-hal-common/src/usb/cdc_acm.rs +++ /dev/null | |||
| @@ -1,338 +0,0 @@ | |||
| 1 | // Copied from https://github.com/mvirkkunen/usbd-serial | ||
| 2 | #![allow(dead_code)] | ||
| 3 | |||
| 4 | use core::convert::TryInto; | ||
| 5 | use core::mem; | ||
| 6 | use usb_device::class_prelude::*; | ||
| 7 | use usb_device::Result; | ||
| 8 | |||
| 9 | /// This should be used as `device_class` when building the `UsbDevice`. | ||
| 10 | pub const USB_CLASS_CDC: u8 = 0x02; | ||
| 11 | |||
| 12 | const USB_CLASS_CDC_DATA: u8 = 0x0a; | ||
| 13 | const CDC_SUBCLASS_ACM: u8 = 0x02; | ||
| 14 | const CDC_PROTOCOL_NONE: u8 = 0x00; | ||
| 15 | |||
| 16 | const CS_INTERFACE: u8 = 0x24; | ||
| 17 | const CDC_TYPE_HEADER: u8 = 0x00; | ||
| 18 | const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01; | ||
| 19 | const CDC_TYPE_ACM: u8 = 0x02; | ||
| 20 | const CDC_TYPE_UNION: u8 = 0x06; | ||
| 21 | |||
| 22 | const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00; | ||
| 23 | #[allow(unused)] | ||
| 24 | const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01; | ||
| 25 | const REQ_SET_LINE_CODING: u8 = 0x20; | ||
| 26 | const REQ_GET_LINE_CODING: u8 = 0x21; | ||
| 27 | const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | ||
| 28 | |||
| 29 | /// Packet level implementation of a CDC-ACM serial port. | ||
| 30 | /// | ||
| 31 | /// This class can be used directly and it has the least overhead due to directly reading and | ||
| 32 | /// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial | ||
| 33 | /// port. The following constraints must be followed if you use this class directly: | ||
| 34 | /// | ||
| 35 | /// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the | ||
| 36 | /// method will return a `WouldBlock` error if there is no packet to be read. | ||
| 37 | /// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the | ||
| 38 | /// method will return a `WouldBlock` error if the previous packet has not been sent yet. | ||
| 39 | /// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the | ||
| 40 | /// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP) | ||
| 41 | /// can be sent if there is no other data to send. This is because USB bulk transactions must be | ||
| 42 | /// terminated with a short packet, even if the bulk endpoint is used for stream-like data. | ||
| 43 | pub struct CdcAcmClass<'a, B: UsbBus> { | ||
| 44 | comm_if: InterfaceNumber, | ||
| 45 | comm_ep: EndpointIn<'a, B>, | ||
| 46 | data_if: InterfaceNumber, | ||
| 47 | read_ep: EndpointOut<'a, B>, | ||
| 48 | write_ep: EndpointIn<'a, B>, | ||
| 49 | line_coding: LineCoding, | ||
| 50 | dtr: bool, | ||
| 51 | rts: bool, | ||
| 52 | } | ||
| 53 | |||
| 54 | impl<B: UsbBus> CdcAcmClass<'_, B> { | ||
| 55 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For | ||
| 56 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. | ||
| 57 | pub fn new(alloc: &UsbBusAllocator<B>, max_packet_size: u16) -> CdcAcmClass<'_, B> { | ||
| 58 | CdcAcmClass { | ||
| 59 | comm_if: alloc.interface(), | ||
| 60 | comm_ep: alloc.interrupt(8, 255), | ||
| 61 | data_if: alloc.interface(), | ||
| 62 | read_ep: alloc.bulk(max_packet_size), | ||
| 63 | write_ep: alloc.bulk(max_packet_size), | ||
| 64 | line_coding: LineCoding { | ||
| 65 | stop_bits: StopBits::One, | ||
| 66 | data_bits: 8, | ||
| 67 | parity_type: ParityType::None, | ||
| 68 | data_rate: 8_000, | ||
| 69 | }, | ||
| 70 | dtr: false, | ||
| 71 | rts: false, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Gets the maximum packet size in bytes. | ||
| 76 | pub fn max_packet_size(&self) -> u16 { | ||
| 77 | // The size is the same for both endpoints. | ||
| 78 | self.read_ep.max_packet_size() | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Gets the current line coding. The line coding contains information that's mainly relevant | ||
| 82 | /// for USB to UART serial port emulators, and can be ignored if not relevant. | ||
| 83 | pub fn line_coding(&self) -> &LineCoding { | ||
| 84 | &self.line_coding | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Gets the DTR (data terminal ready) state | ||
| 88 | pub fn dtr(&self) -> bool { | ||
| 89 | self.dtr | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Gets the RTS (request to send) state | ||
| 93 | pub fn rts(&self) -> bool { | ||
| 94 | self.rts | ||
| 95 | } | ||
| 96 | |||
| 97 | /// Writes a single packet into the IN endpoint. | ||
| 98 | pub fn write_packet(&mut self, data: &[u8]) -> Result<usize> { | ||
| 99 | self.write_ep.write(data) | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Reads a single packet from the OUT endpoint. | ||
| 103 | pub fn read_packet(&mut self, data: &mut [u8]) -> Result<usize> { | ||
| 104 | self.read_ep.read(data) | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Gets the address of the IN endpoint. | ||
| 108 | pub fn write_ep_address(&self) -> EndpointAddress { | ||
| 109 | self.write_ep.address() | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Gets the address of the OUT endpoint. | ||
| 113 | pub fn read_ep_address(&self) -> EndpointAddress { | ||
| 114 | self.read_ep.address() | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> { | ||
| 119 | fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { | ||
| 120 | writer.iad( | ||
| 121 | self.comm_if, | ||
| 122 | 2, | ||
| 123 | USB_CLASS_CDC, | ||
| 124 | CDC_SUBCLASS_ACM, | ||
| 125 | CDC_PROTOCOL_NONE, | ||
| 126 | )?; | ||
| 127 | |||
| 128 | writer.interface( | ||
| 129 | self.comm_if, | ||
| 130 | USB_CLASS_CDC, | ||
| 131 | CDC_SUBCLASS_ACM, | ||
| 132 | CDC_PROTOCOL_NONE, | ||
| 133 | )?; | ||
| 134 | |||
| 135 | writer.write( | ||
| 136 | CS_INTERFACE, | ||
| 137 | &[ | ||
| 138 | CDC_TYPE_HEADER, // bDescriptorSubtype | ||
| 139 | 0x10, | ||
| 140 | 0x01, // bcdCDC (1.10) | ||
| 141 | ], | ||
| 142 | )?; | ||
| 143 | |||
| 144 | writer.write( | ||
| 145 | CS_INTERFACE, | ||
| 146 | &[ | ||
| 147 | CDC_TYPE_ACM, // bDescriptorSubtype | ||
| 148 | 0x00, // bmCapabilities | ||
| 149 | ], | ||
| 150 | )?; | ||
| 151 | |||
| 152 | writer.write( | ||
| 153 | CS_INTERFACE, | ||
| 154 | &[ | ||
| 155 | CDC_TYPE_UNION, // bDescriptorSubtype | ||
| 156 | self.comm_if.into(), // bControlInterface | ||
| 157 | self.data_if.into(), // bSubordinateInterface | ||
| 158 | ], | ||
| 159 | )?; | ||
| 160 | |||
| 161 | writer.write( | ||
| 162 | CS_INTERFACE, | ||
| 163 | &[ | ||
| 164 | CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype | ||
| 165 | 0x00, // bmCapabilities | ||
| 166 | self.data_if.into(), // bDataInterface | ||
| 167 | ], | ||
| 168 | )?; | ||
| 169 | |||
| 170 | writer.endpoint(&self.comm_ep)?; | ||
| 171 | |||
| 172 | writer.interface(self.data_if, USB_CLASS_CDC_DATA, 0x00, 0x00)?; | ||
| 173 | |||
| 174 | writer.endpoint(&self.write_ep)?; | ||
| 175 | writer.endpoint(&self.read_ep)?; | ||
| 176 | |||
| 177 | Ok(()) | ||
| 178 | } | ||
| 179 | |||
| 180 | fn reset(&mut self) { | ||
| 181 | self.line_coding = LineCoding::default(); | ||
| 182 | self.dtr = false; | ||
| 183 | self.rts = false; | ||
| 184 | } | ||
| 185 | |||
| 186 | fn control_in(&mut self, xfer: ControlIn<B>) { | ||
| 187 | let req = xfer.request(); | ||
| 188 | |||
| 189 | if !(req.request_type == control::RequestType::Class | ||
| 190 | && req.recipient == control::Recipient::Interface | ||
| 191 | && req.index == u8::from(self.comm_if) as u16) | ||
| 192 | { | ||
| 193 | return; | ||
| 194 | } | ||
| 195 | |||
| 196 | match req.request { | ||
| 197 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. | ||
| 198 | REQ_GET_LINE_CODING if req.length == 7 => { | ||
| 199 | xfer.accept(|data| { | ||
| 200 | data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); | ||
| 201 | data[4] = self.line_coding.stop_bits as u8; | ||
| 202 | data[5] = self.line_coding.parity_type as u8; | ||
| 203 | data[6] = self.line_coding.data_bits; | ||
| 204 | |||
| 205 | Ok(7) | ||
| 206 | }) | ||
| 207 | .ok(); | ||
| 208 | } | ||
| 209 | _ => { | ||
| 210 | xfer.reject().ok(); | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | fn control_out(&mut self, xfer: ControlOut<B>) { | ||
| 216 | let req = xfer.request(); | ||
| 217 | |||
| 218 | if !(req.request_type == control::RequestType::Class | ||
| 219 | && req.recipient == control::Recipient::Interface | ||
| 220 | && req.index == u8::from(self.comm_if) as u16) | ||
| 221 | { | ||
| 222 | return; | ||
| 223 | } | ||
| 224 | |||
| 225 | match req.request { | ||
| 226 | REQ_SEND_ENCAPSULATED_COMMAND => { | ||
| 227 | // We don't actually support encapsulated commands but pretend we do for standards | ||
| 228 | // compatibility. | ||
| 229 | xfer.accept().ok(); | ||
| 230 | } | ||
| 231 | REQ_SET_LINE_CODING if xfer.data().len() >= 7 => { | ||
| 232 | self.line_coding.data_rate = | ||
| 233 | u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap()); | ||
| 234 | self.line_coding.stop_bits = xfer.data()[4].into(); | ||
| 235 | self.line_coding.parity_type = xfer.data()[5].into(); | ||
| 236 | self.line_coding.data_bits = xfer.data()[6]; | ||
| 237 | |||
| 238 | xfer.accept().ok(); | ||
| 239 | } | ||
| 240 | REQ_SET_CONTROL_LINE_STATE => { | ||
| 241 | self.dtr = (req.value & 0x0001) != 0; | ||
| 242 | self.rts = (req.value & 0x0002) != 0; | ||
| 243 | |||
| 244 | xfer.accept().ok(); | ||
| 245 | } | ||
| 246 | _ => { | ||
| 247 | xfer.reject().ok(); | ||
| 248 | } | ||
| 249 | }; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | /// Number of stop bits for LineCoding | ||
| 254 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
| 255 | pub enum StopBits { | ||
| 256 | /// 1 stop bit | ||
| 257 | One = 0, | ||
| 258 | |||
| 259 | /// 1.5 stop bits | ||
| 260 | OnePointFive = 1, | ||
| 261 | |||
| 262 | /// 2 stop bits | ||
| 263 | Two = 2, | ||
| 264 | } | ||
| 265 | |||
| 266 | impl From<u8> for StopBits { | ||
| 267 | fn from(value: u8) -> Self { | ||
| 268 | if value <= 2 { | ||
| 269 | unsafe { mem::transmute(value) } | ||
| 270 | } else { | ||
| 271 | StopBits::One | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Parity for LineCoding | ||
| 277 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
| 278 | pub enum ParityType { | ||
| 279 | None = 0, | ||
| 280 | Odd = 1, | ||
| 281 | Event = 2, | ||
| 282 | Mark = 3, | ||
| 283 | Space = 4, | ||
| 284 | } | ||
| 285 | |||
| 286 | impl From<u8> for ParityType { | ||
| 287 | fn from(value: u8) -> Self { | ||
| 288 | if value <= 4 { | ||
| 289 | unsafe { mem::transmute(value) } | ||
| 290 | } else { | ||
| 291 | ParityType::None | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | /// Line coding parameters | ||
| 297 | /// | ||
| 298 | /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can | ||
| 299 | /// be ignored if you don't plan to interface with a physical UART. | ||
| 300 | pub struct LineCoding { | ||
| 301 | stop_bits: StopBits, | ||
| 302 | data_bits: u8, | ||
| 303 | parity_type: ParityType, | ||
| 304 | data_rate: u32, | ||
| 305 | } | ||
| 306 | |||
| 307 | impl LineCoding { | ||
| 308 | /// Gets the number of stop bits for UART communication. | ||
| 309 | pub fn stop_bits(&self) -> StopBits { | ||
| 310 | self.stop_bits | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Gets the number of data bits for UART communication. | ||
| 314 | pub fn data_bits(&self) -> u8 { | ||
| 315 | self.data_bits | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Gets the parity type for UART communication. | ||
| 319 | pub fn parity_type(&self) -> ParityType { | ||
| 320 | self.parity_type | ||
| 321 | } | ||
| 322 | |||
| 323 | /// Gets the data rate in bits per second for UART communication. | ||
| 324 | pub fn data_rate(&self) -> u32 { | ||
| 325 | self.data_rate | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | impl Default for LineCoding { | ||
| 330 | fn default() -> Self { | ||
| 331 | LineCoding { | ||
| 332 | stop_bits: StopBits::One, | ||
| 333 | data_bits: 8, | ||
| 334 | parity_type: ParityType::None, | ||
| 335 | data_rate: 8_000, | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
diff --git a/embassy-hal-common/src/usb/mod.rs b/embassy-hal-common/src/usb/mod.rs deleted file mode 100644 index bab72d8b6..000000000 --- a/embassy-hal-common/src/usb/mod.rs +++ /dev/null | |||
| @@ -1,267 +0,0 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::pin::Pin; | ||
| 4 | |||
| 5 | use usb_device::bus::UsbBus; | ||
| 6 | use usb_device::class::UsbClass; | ||
| 7 | use usb_device::device::UsbDevice; | ||
| 8 | |||
| 9 | mod cdc_acm; | ||
| 10 | pub mod usb_serial; | ||
| 11 | |||
| 12 | use crate::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; | ||
| 13 | use embassy::interrupt::Interrupt; | ||
| 14 | pub use usb_serial::{ReadInterface, UsbSerial, WriteInterface}; | ||
| 15 | |||
| 16 | /// Marker trait to mark an interrupt to be used with the [`Usb`] abstraction. | ||
| 17 | pub unsafe trait USBInterrupt: Interrupt + Send {} | ||
| 18 | |||
| 19 | pub struct State<'bus, B, T, I>(StateStorage<StateInner<'bus, B, T, I>>) | ||
| 20 | where | ||
| 21 | B: UsbBus, | ||
| 22 | T: ClassSet<B>, | ||
| 23 | I: USBInterrupt; | ||
| 24 | |||
| 25 | impl<'bus, B, T, I> State<'bus, B, T, I> | ||
| 26 | where | ||
| 27 | B: UsbBus, | ||
| 28 | T: ClassSet<B>, | ||
| 29 | I: USBInterrupt, | ||
| 30 | { | ||
| 31 | pub fn new() -> Self { | ||
| 32 | Self(StateStorage::new()) | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | pub(crate) struct StateInner<'bus, B, T, I> | ||
| 37 | where | ||
| 38 | B: UsbBus, | ||
| 39 | T: ClassSet<B>, | ||
| 40 | I: USBInterrupt, | ||
| 41 | { | ||
| 42 | device: UsbDevice<'bus, B>, | ||
| 43 | pub(crate) classes: T, | ||
| 44 | _interrupt: PhantomData<I>, | ||
| 45 | } | ||
| 46 | |||
| 47 | pub struct Usb<'bus, B, T, I> | ||
| 48 | where | ||
| 49 | B: UsbBus, | ||
| 50 | T: ClassSet<B>, | ||
| 51 | I: USBInterrupt, | ||
| 52 | { | ||
| 53 | // Don't you dare moving out `PeripheralMutex` | ||
| 54 | inner: RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, I>>>, | ||
| 55 | } | ||
| 56 | |||
| 57 | impl<'bus, B, T, I> Usb<'bus, B, T, I> | ||
| 58 | where | ||
| 59 | B: UsbBus, | ||
| 60 | T: ClassSet<B>, | ||
| 61 | I: USBInterrupt, | ||
| 62 | { | ||
| 63 | /// safety: the returned instance is not leak-safe | ||
| 64 | pub unsafe fn new<S: IntoClassSet<B, T>>( | ||
| 65 | state: &'bus mut State<'bus, B, T, I>, | ||
| 66 | device: UsbDevice<'bus, B>, | ||
| 67 | class_set: S, | ||
| 68 | irq: I, | ||
| 69 | ) -> Self { | ||
| 70 | let mutex = PeripheralMutex::new_unchecked(irq, &mut state.0, || StateInner { | ||
| 71 | device, | ||
| 72 | classes: class_set.into_class_set(), | ||
| 73 | _interrupt: PhantomData, | ||
| 74 | }); | ||
| 75 | Self { | ||
| 76 | inner: RefCell::new(mutex), | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I> | ||
| 82 | where | ||
| 83 | B: UsbBus, | ||
| 84 | T: ClassSet<B> + SerialState<'bus, 'c, B, Index0>, | ||
| 85 | I: USBInterrupt, | ||
| 86 | { | ||
| 87 | /// Take a serial class that was passed as the first class in a tuple | ||
| 88 | pub fn take_serial_0<'a>( | ||
| 89 | self: Pin<&'a Self>, | ||
| 90 | ) -> ( | ||
| 91 | ReadInterface<'a, 'bus, 'c, Index0, B, T, I>, | ||
| 92 | WriteInterface<'a, 'bus, 'c, Index0, B, T, I>, | ||
| 93 | ) { | ||
| 94 | let this = self.get_ref(); | ||
| 95 | |||
| 96 | let r = ReadInterface { | ||
| 97 | inner: &this.inner, | ||
| 98 | _buf_lifetime: PhantomData, | ||
| 99 | _index: PhantomData, | ||
| 100 | }; | ||
| 101 | |||
| 102 | let w = WriteInterface { | ||
| 103 | inner: &this.inner, | ||
| 104 | _buf_lifetime: PhantomData, | ||
| 105 | _index: PhantomData, | ||
| 106 | }; | ||
| 107 | (r, w) | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I> | ||
| 112 | where | ||
| 113 | B: UsbBus, | ||
| 114 | T: ClassSet<B> + SerialState<'bus, 'c, B, Index1>, | ||
| 115 | I: USBInterrupt, | ||
| 116 | { | ||
| 117 | /// Take a serial class that was passed as the second class in a tuple | ||
| 118 | pub fn take_serial_1<'a>( | ||
| 119 | self: Pin<&'a Self>, | ||
| 120 | ) -> ( | ||
| 121 | ReadInterface<'a, 'bus, 'c, Index1, B, T, I>, | ||
| 122 | WriteInterface<'a, 'bus, 'c, Index1, B, T, I>, | ||
| 123 | ) { | ||
| 124 | let this = self.get_ref(); | ||
| 125 | |||
| 126 | let r = ReadInterface { | ||
| 127 | inner: &this.inner, | ||
| 128 | _buf_lifetime: PhantomData, | ||
| 129 | _index: PhantomData, | ||
| 130 | }; | ||
| 131 | |||
| 132 | let w = WriteInterface { | ||
| 133 | inner: &this.inner, | ||
| 134 | _buf_lifetime: PhantomData, | ||
| 135 | _index: PhantomData, | ||
| 136 | }; | ||
| 137 | (r, w) | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | impl<'bus, B, T, I> PeripheralState for StateInner<'bus, B, T, I> | ||
| 142 | where | ||
| 143 | B: UsbBus, | ||
| 144 | T: ClassSet<B>, | ||
| 145 | I: USBInterrupt, | ||
| 146 | { | ||
| 147 | type Interrupt = I; | ||
| 148 | fn on_interrupt(&mut self) { | ||
| 149 | self.classes.poll_all(&mut self.device); | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | pub trait ClassSet<B: UsbBus>: Send { | ||
| 154 | fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool; | ||
| 155 | } | ||
| 156 | |||
| 157 | pub trait IntoClassSet<B: UsbBus, C: ClassSet<B>> { | ||
| 158 | fn into_class_set(self) -> C; | ||
| 159 | } | ||
| 160 | |||
| 161 | pub struct ClassSet1<B, C1> | ||
| 162 | where | ||
| 163 | B: UsbBus, | ||
| 164 | C1: UsbClass<B>, | ||
| 165 | { | ||
| 166 | class: C1, | ||
| 167 | _bus: PhantomData<B>, | ||
| 168 | } | ||
| 169 | |||
| 170 | pub struct ClassSet2<B, C1, C2> | ||
| 171 | where | ||
| 172 | B: UsbBus, | ||
| 173 | C1: UsbClass<B>, | ||
| 174 | C2: UsbClass<B>, | ||
| 175 | { | ||
| 176 | class1: C1, | ||
| 177 | class2: C2, | ||
| 178 | _bus: PhantomData<B>, | ||
| 179 | } | ||
| 180 | |||
| 181 | /// The first class into a [`ClassSet`] | ||
| 182 | pub struct Index0; | ||
| 183 | |||
| 184 | /// The second class into a [`ClassSet`] | ||
| 185 | pub struct Index1; | ||
| 186 | |||
| 187 | impl<B, C1> ClassSet<B> for ClassSet1<B, C1> | ||
| 188 | where | ||
| 189 | B: UsbBus + Send, | ||
| 190 | C1: UsbClass<B> + Send, | ||
| 191 | { | ||
| 192 | fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { | ||
| 193 | device.poll(&mut [&mut self.class]) | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | impl<B, C1, C2> ClassSet<B> for ClassSet2<B, C1, C2> | ||
| 198 | where | ||
| 199 | B: UsbBus + Send, | ||
| 200 | C1: UsbClass<B> + Send, | ||
| 201 | C2: UsbClass<B> + Send, | ||
| 202 | { | ||
| 203 | fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { | ||
| 204 | device.poll(&mut [&mut self.class1, &mut self.class2]) | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | impl<B, C1> IntoClassSet<B, ClassSet1<B, C1>> for C1 | ||
| 209 | where | ||
| 210 | B: UsbBus + Send, | ||
| 211 | C1: UsbClass<B> + Send, | ||
| 212 | { | ||
| 213 | fn into_class_set(self) -> ClassSet1<B, C1> { | ||
| 214 | ClassSet1 { | ||
| 215 | class: self, | ||
| 216 | _bus: PhantomData, | ||
| 217 | } | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | impl<B, C1, C2> IntoClassSet<B, ClassSet2<B, C1, C2>> for (C1, C2) | ||
| 222 | where | ||
| 223 | B: UsbBus + Send, | ||
| 224 | C1: UsbClass<B> + Send, | ||
| 225 | C2: UsbClass<B> + Send, | ||
| 226 | { | ||
| 227 | fn into_class_set(self) -> ClassSet2<B, C1, C2> { | ||
| 228 | ClassSet2 { | ||
| 229 | class1: self.0, | ||
| 230 | class2: self.1, | ||
| 231 | _bus: PhantomData, | ||
| 232 | } | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Trait for a USB State that has a serial class inside | ||
| 237 | pub trait SerialState<'bus, 'a, B: UsbBus, I> { | ||
| 238 | fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B>; | ||
| 239 | } | ||
| 240 | |||
| 241 | impl<'bus, 'a, B: UsbBus> SerialState<'bus, 'a, B, Index0> | ||
| 242 | for ClassSet1<B, UsbSerial<'bus, 'a, B>> | ||
| 243 | { | ||
| 244 | fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { | ||
| 245 | &mut self.class | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | impl<'bus, 'a, B, C2> SerialState<'bus, 'a, B, Index0> for ClassSet2<B, UsbSerial<'bus, 'a, B>, C2> | ||
| 250 | where | ||
| 251 | B: UsbBus, | ||
| 252 | C2: UsbClass<B>, | ||
| 253 | { | ||
| 254 | fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { | ||
| 255 | &mut self.class1 | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | impl<'bus, 'a, B, C1> SerialState<'bus, 'a, B, Index1> for ClassSet2<B, C1, UsbSerial<'bus, 'a, B>> | ||
| 260 | where | ||
| 261 | B: UsbBus, | ||
| 262 | C1: UsbClass<B>, | ||
| 263 | { | ||
| 264 | fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { | ||
| 265 | &mut self.class2 | ||
| 266 | } | ||
| 267 | } | ||
diff --git a/embassy-hal-common/src/usb/usb_serial.rs b/embassy-hal-common/src/usb/usb_serial.rs deleted file mode 100644 index 94f687890..000000000 --- a/embassy-hal-common/src/usb/usb_serial.rs +++ /dev/null | |||
| @@ -1,345 +0,0 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::marker::{PhantomData, Unpin}; | ||
| 3 | use core::pin::Pin; | ||
| 4 | use core::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use embassy::io::{self, AsyncBufRead, AsyncWrite}; | ||
| 7 | use embassy::waitqueue::WakerRegistration; | ||
| 8 | use usb_device::bus::UsbBus; | ||
| 9 | use usb_device::class_prelude::*; | ||
| 10 | use usb_device::UsbError; | ||
| 11 | |||
| 12 | use super::cdc_acm::CdcAcmClass; | ||
| 13 | use super::StateInner; | ||
| 14 | use crate::peripheral::PeripheralMutex; | ||
| 15 | use crate::ring_buffer::RingBuffer; | ||
| 16 | use crate::usb::{ClassSet, SerialState, USBInterrupt}; | ||
| 17 | |||
| 18 | pub struct ReadInterface<'a, 'bus, 'c, I, B, T, INT> | ||
| 19 | where | ||
| 20 | I: Unpin, | ||
| 21 | B: UsbBus, | ||
| 22 | T: SerialState<'bus, 'c, B, I> + ClassSet<B>, | ||
| 23 | INT: USBInterrupt, | ||
| 24 | { | ||
| 25 | // Don't you dare moving out `PeripheralMutex` | ||
| 26 | pub(crate) inner: &'a RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, INT>>>, | ||
| 27 | pub(crate) _buf_lifetime: PhantomData<&'c T>, | ||
| 28 | pub(crate) _index: PhantomData<I>, | ||
| 29 | } | ||
| 30 | |||
| 31 | /// Write interface for USB CDC_ACM | ||
| 32 | /// | ||
| 33 | /// This interface is buffered, meaning that after the write returns the bytes might not be fully | ||
| 34 | /// on the wire just yet | ||
| 35 | pub struct WriteInterface<'a, 'bus, 'c, I, B, T, INT> | ||
| 36 | where | ||
| 37 | I: Unpin, | ||
| 38 | B: UsbBus, | ||
| 39 | T: SerialState<'bus, 'c, B, I> + ClassSet<B>, | ||
| 40 | INT: USBInterrupt, | ||
| 41 | { | ||
| 42 | // Don't you dare moving out `PeripheralMutex` | ||
| 43 | pub(crate) inner: &'a RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, INT>>>, | ||
| 44 | pub(crate) _buf_lifetime: PhantomData<&'c T>, | ||
| 45 | pub(crate) _index: PhantomData<I>, | ||
| 46 | } | ||
| 47 | |||
| 48 | impl<'a, 'bus, 'c, I, B, T, INT> AsyncBufRead for ReadInterface<'a, 'bus, 'c, I, B, T, INT> | ||
| 49 | where | ||
| 50 | I: Unpin, | ||
| 51 | B: UsbBus, | ||
| 52 | T: SerialState<'bus, 'c, B, I> + ClassSet<B>, | ||
| 53 | INT: USBInterrupt, | ||
| 54 | { | ||
| 55 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { | ||
| 56 | let this = self.get_mut(); | ||
| 57 | let mut mutex = this.inner.borrow_mut(); | ||
| 58 | mutex.with(|state| { | ||
| 59 | let serial = state.classes.get_serial(); | ||
| 60 | let serial = Pin::new(serial); | ||
| 61 | |||
| 62 | match serial.poll_fill_buf(cx) { | ||
| 63 | Poll::Ready(Ok(buf)) => { | ||
| 64 | let buf: &[u8] = buf; | ||
| 65 | // NOTE(unsafe) This part of the buffer won't be modified until the user calls | ||
| 66 | // consume, which will invalidate this ref | ||
| 67 | let buf: &[u8] = unsafe { core::mem::transmute(buf) }; | ||
| 68 | Poll::Ready(Ok(buf)) | ||
| 69 | } | ||
| 70 | Poll::Ready(Err(_)) => Poll::Ready(Err(io::Error::Other)), | ||
| 71 | Poll::Pending => Poll::Pending, | ||
| 72 | } | ||
| 73 | }) | ||
| 74 | } | ||
| 75 | |||
| 76 | fn consume(self: Pin<&mut Self>, amt: usize) { | ||
| 77 | let this = self.get_mut(); | ||
| 78 | let mut mutex = this.inner.borrow_mut(); | ||
| 79 | mutex.with(|state| { | ||
| 80 | let serial = state.classes.get_serial(); | ||
| 81 | let serial = Pin::new(serial); | ||
| 82 | |||
| 83 | serial.consume(amt); | ||
| 84 | }) | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | impl<'a, 'bus, 'c, I, B, T, INT> AsyncWrite for WriteInterface<'a, 'bus, 'c, I, B, T, INT> | ||
| 89 | where | ||
| 90 | I: Unpin, | ||
| 91 | B: UsbBus, | ||
| 92 | T: SerialState<'bus, 'c, B, I> + ClassSet<B>, | ||
| 93 | INT: USBInterrupt, | ||
| 94 | { | ||
| 95 | fn poll_write( | ||
| 96 | self: Pin<&mut Self>, | ||
| 97 | cx: &mut Context<'_>, | ||
| 98 | buf: &[u8], | ||
| 99 | ) -> Poll<io::Result<usize>> { | ||
| 100 | let this = self.get_mut(); | ||
| 101 | let mut mutex = this.inner.borrow_mut(); | ||
| 102 | mutex.with(|state| { | ||
| 103 | let serial = state.classes.get_serial(); | ||
| 104 | let serial = Pin::new(serial); | ||
| 105 | |||
| 106 | serial.poll_write(cx, buf) | ||
| 107 | }) | ||
| 108 | } | ||
| 109 | |||
| 110 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
| 111 | let this = self.get_mut(); | ||
| 112 | let mut mutex = this.inner.borrow_mut(); | ||
| 113 | mutex.with(|state| { | ||
| 114 | let serial = state.classes.get_serial(); | ||
| 115 | let serial = Pin::new(serial); | ||
| 116 | |||
| 117 | serial.poll_flush(cx) | ||
| 118 | }) | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | pub struct UsbSerial<'bus, 'a, B: UsbBus> { | ||
| 123 | inner: CdcAcmClass<'bus, B>, | ||
| 124 | read_buf: RingBuffer<'a>, | ||
| 125 | write_buf: RingBuffer<'a>, | ||
| 126 | read_waker: WakerRegistration, | ||
| 127 | write_waker: WakerRegistration, | ||
| 128 | write_state: WriteState, | ||
| 129 | read_error: bool, | ||
| 130 | write_error: bool, | ||
| 131 | } | ||
| 132 | |||
| 133 | impl<'bus, 'a, B: UsbBus> AsyncBufRead for UsbSerial<'bus, 'a, B> { | ||
| 134 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { | ||
| 135 | let this = self.get_mut(); | ||
| 136 | |||
| 137 | if this.read_error { | ||
| 138 | this.read_error = false; | ||
| 139 | return Poll::Ready(Err(io::Error::Other)); | ||
| 140 | } | ||
| 141 | |||
| 142 | let buf = this.read_buf.pop_buf(); | ||
| 143 | if buf.is_empty() { | ||
| 144 | this.read_waker.register(cx.waker()); | ||
| 145 | return Poll::Pending; | ||
| 146 | } | ||
| 147 | Poll::Ready(Ok(buf)) | ||
| 148 | } | ||
| 149 | |||
| 150 | fn consume(self: Pin<&mut Self>, amt: usize) { | ||
| 151 | self.get_mut().read_buf.pop(amt); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | impl<'bus, 'a, B: UsbBus> AsyncWrite for UsbSerial<'bus, 'a, B> { | ||
| 156 | fn poll_write( | ||
| 157 | self: Pin<&mut Self>, | ||
| 158 | cx: &mut Context<'_>, | ||
| 159 | buf: &[u8], | ||
| 160 | ) -> Poll<io::Result<usize>> { | ||
| 161 | let this = self.get_mut(); | ||
| 162 | |||
| 163 | if this.write_error { | ||
| 164 | this.write_error = false; | ||
| 165 | return Poll::Ready(Err(io::Error::Other)); | ||
| 166 | } | ||
| 167 | |||
| 168 | let write_buf = this.write_buf.push_buf(); | ||
| 169 | if write_buf.is_empty() { | ||
| 170 | trace!("buf full, registering waker"); | ||
| 171 | this.write_waker.register(cx.waker()); | ||
| 172 | return Poll::Pending; | ||
| 173 | } | ||
| 174 | |||
| 175 | let count = write_buf.len().min(buf.len()); | ||
| 176 | write_buf[..count].copy_from_slice(&buf[..count]); | ||
| 177 | this.write_buf.push(count); | ||
| 178 | |||
| 179 | this.flush_write(); | ||
| 180 | Poll::Ready(Ok(count)) | ||
| 181 | } | ||
| 182 | |||
| 183 | fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
| 184 | Poll::Ready(Ok(())) | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | /// Keeps track of the type of the last written packet. | ||
| 189 | enum WriteState { | ||
| 190 | /// No packets in-flight | ||
| 191 | Idle, | ||
| 192 | |||
| 193 | /// Short packet currently in-flight | ||
| 194 | Short, | ||
| 195 | |||
| 196 | /// Full packet current in-flight. A full packet must be followed by a short packet for the host | ||
| 197 | /// OS to see the transaction. The data is the number of subsequent full packets sent so far. A | ||
| 198 | /// short packet is forced every SHORT_PACKET_INTERVAL packets so that the OS sees data in a | ||
| 199 | /// timely manner. | ||
| 200 | Full(usize), | ||
| 201 | } | ||
| 202 | |||
| 203 | impl<'bus, 'a, B: UsbBus> UsbSerial<'bus, 'a, B> { | ||
| 204 | pub fn new( | ||
| 205 | alloc: &'bus UsbBusAllocator<B>, | ||
| 206 | read_buf: &'a mut [u8], | ||
| 207 | write_buf: &'a mut [u8], | ||
| 208 | ) -> Self { | ||
| 209 | Self { | ||
| 210 | inner: CdcAcmClass::new(alloc, 64), | ||
| 211 | read_buf: RingBuffer::new(read_buf), | ||
| 212 | write_buf: RingBuffer::new(write_buf), | ||
| 213 | read_waker: WakerRegistration::new(), | ||
| 214 | write_waker: WakerRegistration::new(), | ||
| 215 | write_state: WriteState::Idle, | ||
| 216 | read_error: false, | ||
| 217 | write_error: false, | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | fn flush_write(&mut self) { | ||
| 222 | /// If this many full size packets have been sent in a row, a short packet will be sent so that the | ||
| 223 | /// host sees the data in a timely manner. | ||
| 224 | const SHORT_PACKET_INTERVAL: usize = 10; | ||
| 225 | |||
| 226 | let full_size_packets = match self.write_state { | ||
| 227 | WriteState::Full(c) => c, | ||
| 228 | _ => 0, | ||
| 229 | }; | ||
| 230 | |||
| 231 | let ep_size = self.inner.max_packet_size() as usize; | ||
| 232 | let max_size = if full_size_packets > SHORT_PACKET_INTERVAL { | ||
| 233 | ep_size - 1 | ||
| 234 | } else { | ||
| 235 | ep_size | ||
| 236 | }; | ||
| 237 | |||
| 238 | let buf = { | ||
| 239 | let buf = self.write_buf.pop_buf(); | ||
| 240 | if buf.len() > max_size { | ||
| 241 | &buf[..max_size] | ||
| 242 | } else { | ||
| 243 | buf | ||
| 244 | } | ||
| 245 | }; | ||
| 246 | |||
| 247 | if !buf.is_empty() { | ||
| 248 | trace!("writing packet len {}", buf.len()); | ||
| 249 | let count = match self.inner.write_packet(buf) { | ||
| 250 | Ok(c) => { | ||
| 251 | trace!("write packet: OK {}", c); | ||
| 252 | c | ||
| 253 | } | ||
| 254 | Err(UsbError::WouldBlock) => { | ||
| 255 | trace!("write packet: WouldBlock"); | ||
| 256 | 0 | ||
| 257 | } | ||
| 258 | Err(_) => { | ||
| 259 | trace!("write packet: error"); | ||
| 260 | self.write_error = true; | ||
| 261 | return; | ||
| 262 | } | ||
| 263 | }; | ||
| 264 | |||
| 265 | if buf.len() == ep_size { | ||
| 266 | self.write_state = WriteState::Full(full_size_packets + 1); | ||
| 267 | } else { | ||
| 268 | self.write_state = WriteState::Short; | ||
| 269 | } | ||
| 270 | self.write_buf.pop(count); | ||
| 271 | } else if full_size_packets > 0 { | ||
| 272 | trace!("writing empty packet"); | ||
| 273 | match self.inner.write_packet(&[]) { | ||
| 274 | Ok(_) => { | ||
| 275 | trace!("write empty packet: OK"); | ||
| 276 | } | ||
| 277 | Err(UsbError::WouldBlock) => { | ||
| 278 | trace!("write empty packet: WouldBlock"); | ||
| 279 | return; | ||
| 280 | } | ||
| 281 | Err(_) => { | ||
| 282 | trace!("write empty packet: Error"); | ||
| 283 | self.write_error = true; | ||
| 284 | return; | ||
| 285 | } | ||
| 286 | } | ||
| 287 | self.write_state = WriteState::Idle; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | impl<B> UsbClass<B> for UsbSerial<'_, '_, B> | ||
| 293 | where | ||
| 294 | B: UsbBus, | ||
| 295 | { | ||
| 296 | fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<(), UsbError> { | ||
| 297 | self.inner.get_configuration_descriptors(writer) | ||
| 298 | } | ||
| 299 | |||
| 300 | fn reset(&mut self) { | ||
| 301 | self.inner.reset(); | ||
| 302 | self.read_buf.clear(); | ||
| 303 | self.write_buf.clear(); | ||
| 304 | self.write_state = WriteState::Idle; | ||
| 305 | self.read_waker.wake(); | ||
| 306 | self.write_waker.wake(); | ||
| 307 | } | ||
| 308 | |||
| 309 | fn endpoint_in_complete(&mut self, addr: EndpointAddress) { | ||
| 310 | trace!("DONE endpoint_in_complete"); | ||
| 311 | if addr == self.inner.write_ep_address() { | ||
| 312 | trace!("DONE writing packet, waking"); | ||
| 313 | self.write_waker.wake(); | ||
| 314 | |||
| 315 | self.flush_write(); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | fn endpoint_out(&mut self, addr: EndpointAddress) { | ||
| 320 | if addr == self.inner.read_ep_address() { | ||
| 321 | let buf = self.read_buf.push_buf(); | ||
| 322 | let count = match self.inner.read_packet(buf) { | ||
| 323 | Ok(c) => c, | ||
| 324 | Err(UsbError::WouldBlock) => 0, | ||
| 325 | Err(_) => { | ||
| 326 | self.read_error = true; | ||
| 327 | return; | ||
| 328 | } | ||
| 329 | }; | ||
| 330 | |||
| 331 | if count > 0 { | ||
| 332 | self.read_buf.push(count); | ||
| 333 | self.read_waker.wake(); | ||
| 334 | } | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | fn control_in(&mut self, xfer: ControlIn<B>) { | ||
| 339 | self.inner.control_in(xfer); | ||
| 340 | } | ||
| 341 | |||
| 342 | fn control_out(&mut self, xfer: ControlOut<B>) { | ||
| 343 | self.inner.control_out(xfer); | ||
| 344 | } | ||
| 345 | } | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8152b07d4..ea9ac3b59 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -53,7 +53,6 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa | |||
| 53 | rand_core = "0.6.3" | 53 | rand_core = "0.6.3" |
| 54 | sdio-host = "0.5.0" | 54 | sdio-host = "0.5.0" |
| 55 | embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true } | 55 | embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true } |
| 56 | synopsys-usb-otg = { version = "0.3", features = ["cortex-m", "hs"], optional = true } | ||
| 57 | critical-section = "0.2.5" | 56 | critical-section = "0.2.5" |
| 58 | bare-metal = "1.0.0" | 57 | bare-metal = "1.0.0" |
| 59 | atomic-polyfill = "0.1.5" | 58 | atomic-polyfill = "0.1.5" |
| @@ -76,7 +75,6 @@ net = ["embassy-net", "vcell"] | |||
| 76 | memory-x = ["stm32-metapac/memory-x"] | 75 | memory-x = ["stm32-metapac/memory-x"] |
| 77 | subghz = [] | 76 | subghz = [] |
| 78 | exti = [] | 77 | exti = [] |
| 79 | usb-otg = ["synopsys-usb-otg"] | ||
| 80 | 78 | ||
| 81 | # Features starting with `_` are for internal use only. They're not intended | 79 | # Features starting with `_` are for internal use only. They're not intended |
| 82 | # to be enabled by other crates, and are not covered by semver guarantees. | 80 | # to be enabled by other crates, and are not covered by semver guarantees. |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index aa3e64131..db757764f 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -279,22 +279,22 @@ fn main() { | |||
| 279 | (("dcmi", "HSYNC"), (quote!(crate::dcmi::HSyncPin), quote!())), | 279 | (("dcmi", "HSYNC"), (quote!(crate::dcmi::HSyncPin), quote!())), |
| 280 | (("dcmi", "VSYNC"), (quote!(crate::dcmi::VSyncPin), quote!())), | 280 | (("dcmi", "VSYNC"), (quote!(crate::dcmi::VSyncPin), quote!())), |
| 281 | (("dcmi", "PIXCLK"), (quote!(crate::dcmi::PixClkPin), quote!())), | 281 | (("dcmi", "PIXCLK"), (quote!(crate::dcmi::PixClkPin), quote!())), |
| 282 | (("otgfs", "DP"), (quote!(crate::usb_otg::DpPin), quote!(#[cfg(feature="usb-otg")]))), | 282 | (("otgfs", "DP"), (quote!(crate::usb_otg::DpPin), quote!())), |
| 283 | (("otgfs", "DM"), (quote!(crate::usb_otg::DmPin), quote!(#[cfg(feature="usb-otg")]))), | 283 | (("otgfs", "DM"), (quote!(crate::usb_otg::DmPin), quote!())), |
| 284 | (("otghs", "DP"), (quote!(crate::usb_otg::DpPin), quote!(#[cfg(feature="usb-otg")]))), | 284 | (("otghs", "DP"), (quote!(crate::usb_otg::DpPin), quote!())), |
| 285 | (("otghs", "DM"), (quote!(crate::usb_otg::DmPin), quote!(#[cfg(feature="usb-otg")]))), | 285 | (("otghs", "DM"), (quote!(crate::usb_otg::DmPin), quote!())), |
| 286 | (("otghs", "ULPI_CK"), (quote!(crate::usb_otg::UlpiClkPin), quote!(#[cfg(feature="usb-otg")]))), | 286 | (("otghs", "ULPI_CK"), (quote!(crate::usb_otg::UlpiClkPin), quote!())), |
| 287 | (("otghs", "ULPI_DIR"), (quote!(crate::usb_otg::UlpiDirPin), quote!(#[cfg(feature="usb-otg")]))), | 287 | (("otghs", "ULPI_DIR"), (quote!(crate::usb_otg::UlpiDirPin), quote!())), |
| 288 | (("otghs", "ULPI_NXT"), (quote!(crate::usb_otg::UlpiNxtPin), quote!(#[cfg(feature="usb-otg")]))), | 288 | (("otghs", "ULPI_NXT"), (quote!(crate::usb_otg::UlpiNxtPin), quote!())), |
| 289 | (("otghs", "ULPI_STP"), (quote!(crate::usb_otg::UlpiStpPin), quote!(#[cfg(feature="usb-otg")]))), | 289 | (("otghs", "ULPI_STP"), (quote!(crate::usb_otg::UlpiStpPin), quote!())), |
| 290 | (("otghs", "ULPI_D0"), (quote!(crate::usb_otg::UlpiD0Pin), quote!(#[cfg(feature="usb-otg")]))), | 290 | (("otghs", "ULPI_D0"), (quote!(crate::usb_otg::UlpiD0Pin), quote!())), |
| 291 | (("otghs", "ULPI_D1"), (quote!(crate::usb_otg::UlpiD1Pin), quote!(#[cfg(feature="usb-otg")]))), | 291 | (("otghs", "ULPI_D1"), (quote!(crate::usb_otg::UlpiD1Pin), quote!())), |
| 292 | (("otghs", "ULPI_D2"), (quote!(crate::usb_otg::UlpiD2Pin), quote!(#[cfg(feature="usb-otg")]))), | 292 | (("otghs", "ULPI_D2"), (quote!(crate::usb_otg::UlpiD2Pin), quote!())), |
| 293 | (("otghs", "ULPI_D3"), (quote!(crate::usb_otg::UlpiD3Pin), quote!(#[cfg(feature="usb-otg")]))), | 293 | (("otghs", "ULPI_D3"), (quote!(crate::usb_otg::UlpiD3Pin), quote!())), |
| 294 | (("otghs", "ULPI_D4"), (quote!(crate::usb_otg::UlpiD4Pin), quote!(#[cfg(feature="usb-otg")]))), | 294 | (("otghs", "ULPI_D4"), (quote!(crate::usb_otg::UlpiD4Pin), quote!())), |
| 295 | (("otghs", "ULPI_D5"), (quote!(crate::usb_otg::UlpiD5Pin), quote!(#[cfg(feature="usb-otg")]))), | 295 | (("otghs", "ULPI_D5"), (quote!(crate::usb_otg::UlpiD5Pin), quote!())), |
| 296 | (("otghs", "ULPI_D6"), (quote!(crate::usb_otg::UlpiD6Pin), quote!(#[cfg(feature="usb-otg")]))), | 296 | (("otghs", "ULPI_D6"), (quote!(crate::usb_otg::UlpiD6Pin), quote!())), |
| 297 | (("otghs", "ULPI_D7"), (quote!(crate::usb_otg::UlpiD7Pin), quote!(#[cfg(feature="usb-otg")]))), | 297 | (("otghs", "ULPI_D7"), (quote!(crate::usb_otg::UlpiD7Pin), quote!())), |
| 298 | (("can", "TX"), (quote!(crate::can::TxPin), quote!())), | 298 | (("can", "TX"), (quote!(crate::can::TxPin), quote!())), |
| 299 | (("can", "RX"), (quote!(crate::can::RxPin), quote!())), | 299 | (("can", "RX"), (quote!(crate::can::RxPin), quote!())), |
| 300 | (("eth", "REF_CLK"), (quote!(crate::eth::RefClkPin), quote!(#[cfg(feature="net")]))), | 300 | (("eth", "REF_CLK"), (quote!(crate::eth::RefClkPin), quote!(#[cfg(feature="net")]))), |
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ba426128f..6e9077b4c 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -61,7 +61,7 @@ pub mod sdmmc; | |||
| 61 | pub mod spi; | 61 | pub mod spi; |
| 62 | #[cfg(usart)] | 62 | #[cfg(usart)] |
| 63 | pub mod usart; | 63 | pub mod usart; |
| 64 | #[cfg(feature = "usb-otg")] | 64 | #[cfg(any(otgfs, otghs))] |
| 65 | pub mod usb_otg; | 65 | pub mod usb_otg; |
| 66 | 66 | ||
| 67 | #[cfg(feature = "subghz")] | 67 | #[cfg(feature = "subghz")] |
diff --git a/embassy-stm32/src/usb_otg.rs b/embassy-stm32/src/usb_otg.rs index 8c2c1e99c..21b890d7c 100644 --- a/embassy-stm32/src/usb_otg.rs +++ b/embassy-stm32/src/usb_otg.rs | |||
| @@ -1,15 +1,11 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use embassy::util::Unborrow; | 2 | use embassy::util::Unborrow; |
| 3 | use embassy_hal_common::unborrow; | 3 | use embassy_hal_common::unborrow; |
| 4 | use synopsys_usb_otg::{PhyType, UsbPeripheral}; | ||
| 5 | 4 | ||
| 6 | use crate::gpio::sealed::AFType; | 5 | use crate::gpio::sealed::AFType; |
| 7 | use crate::gpio::Speed; | 6 | use crate::gpio::Speed; |
| 8 | use crate::{peripherals, rcc::RccPeripheral}; | 7 | use crate::{peripherals, rcc::RccPeripheral}; |
| 9 | 8 | ||
| 10 | pub use embassy_hal_common::usb::*; | ||
| 11 | pub use synopsys_usb_otg::UsbBus; | ||
| 12 | |||
| 13 | macro_rules! config_ulpi_pins { | 9 | macro_rules! config_ulpi_pins { |
| 14 | ($($pin:ident),*) => { | 10 | ($($pin:ident),*) => { |
| 15 | unborrow!($($pin),*); | 11 | unborrow!($($pin),*); |
| @@ -23,9 +19,24 @@ macro_rules! config_ulpi_pins { | |||
| 23 | }; | 19 | }; |
| 24 | } | 20 | } |
| 25 | 21 | ||
| 22 | /// USB PHY type | ||
| 23 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
| 24 | pub enum PhyType { | ||
| 25 | /// Internal Full-Speed PHY | ||
| 26 | /// | ||
| 27 | /// Available on most High-Speed peripherals. | ||
| 28 | InternalFullSpeed, | ||
| 29 | /// Internal High-Speed PHY | ||
| 30 | /// | ||
| 31 | /// Available on a few STM32 chips. | ||
| 32 | InternalHighSpeed, | ||
| 33 | /// External ULPI High-Speed PHY | ||
| 34 | ExternalHighSpeed, | ||
| 35 | } | ||
| 36 | |||
| 26 | pub struct UsbOtg<'d, T: Instance> { | 37 | pub struct UsbOtg<'d, T: Instance> { |
| 27 | phantom: PhantomData<&'d mut T>, | 38 | phantom: PhantomData<&'d mut T>, |
| 28 | phy_type: PhyType, | 39 | _phy_type: PhyType, |
| 29 | } | 40 | } |
| 30 | 41 | ||
| 31 | impl<'d, T: Instance> UsbOtg<'d, T> { | 42 | impl<'d, T: Instance> UsbOtg<'d, T> { |
| @@ -44,7 +55,7 @@ impl<'d, T: Instance> UsbOtg<'d, T> { | |||
| 44 | 55 | ||
| 45 | Self { | 56 | Self { |
| 46 | phantom: PhantomData, | 57 | phantom: PhantomData, |
| 47 | phy_type: PhyType::InternalFullSpeed, | 58 | _phy_type: PhyType::InternalFullSpeed, |
| 48 | } | 59 | } |
| 49 | } | 60 | } |
| 50 | 61 | ||
| @@ -71,7 +82,7 @@ impl<'d, T: Instance> UsbOtg<'d, T> { | |||
| 71 | 82 | ||
| 72 | Self { | 83 | Self { |
| 73 | phantom: PhantomData, | 84 | phantom: PhantomData, |
| 74 | phy_type: PhyType::ExternalHighSpeed, | 85 | _phy_type: PhyType::ExternalHighSpeed, |
| 75 | } | 86 | } |
| 76 | } | 87 | } |
| 77 | } | 88 | } |
| @@ -83,29 +94,6 @@ impl<'d, T: Instance> Drop for UsbOtg<'d, T> { | |||
| 83 | } | 94 | } |
| 84 | } | 95 | } |
| 85 | 96 | ||
| 86 | unsafe impl<'d, T: Instance> Send for UsbOtg<'d, T> {} | ||
| 87 | unsafe impl<'d, T: Instance> Sync for UsbOtg<'d, T> {} | ||
| 88 | |||
| 89 | unsafe impl<'d, T: Instance> UsbPeripheral for UsbOtg<'d, T> { | ||
| 90 | const REGISTERS: *const () = T::REGISTERS; | ||
| 91 | const HIGH_SPEED: bool = T::HIGH_SPEED; | ||
| 92 | const FIFO_DEPTH_WORDS: usize = T::FIFO_DEPTH_WORDS; | ||
| 93 | const ENDPOINT_COUNT: usize = T::ENDPOINT_COUNT; | ||
| 94 | |||
| 95 | fn enable() { | ||
| 96 | <T as crate::rcc::sealed::RccPeripheral>::enable(); | ||
| 97 | <T as crate::rcc::sealed::RccPeripheral>::reset(); | ||
| 98 | } | ||
| 99 | |||
| 100 | fn phy_type(&self) -> PhyType { | ||
| 101 | self.phy_type | ||
| 102 | } | ||
| 103 | |||
| 104 | fn ahb_frequency_hz(&self) -> u32 { | ||
| 105 | <T as crate::rcc::sealed::RccPeripheral>::frequency().0 | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | pub(crate) mod sealed { | 97 | pub(crate) mod sealed { |
| 110 | pub trait Instance { | 98 | pub trait Instance { |
| 111 | const REGISTERS: *const (); | 99 | const REGISTERS: *const (); |
| @@ -177,7 +165,7 @@ foreach_peripheral!( | |||
| 177 | const FIFO_DEPTH_WORDS: usize = 512; | 165 | const FIFO_DEPTH_WORDS: usize = 512; |
| 178 | const ENDPOINT_COUNT: usize = 8; | 166 | const ENDPOINT_COUNT: usize = 8; |
| 179 | } else { | 167 | } else { |
| 180 | compile_error!("USB_OTG_FS peripheral is not supported by this chip. Disable \"usb-otg-fs\" feature or select a different chip."); | 168 | compile_error!("USB_OTG_FS peripheral is not supported by this chip."); |
| 181 | } | 169 | } |
| 182 | } | 170 | } |
| 183 | } | 171 | } |
| @@ -214,7 +202,7 @@ foreach_peripheral!( | |||
| 214 | const FIFO_DEPTH_WORDS: usize = 1024; | 202 | const FIFO_DEPTH_WORDS: usize = 1024; |
| 215 | const ENDPOINT_COUNT: usize = 9; | 203 | const ENDPOINT_COUNT: usize = 9; |
| 216 | } else { | 204 | } else { |
| 217 | compile_error!("USB_OTG_HS peripheral is not supported by this chip. Disable \"usb-otg-hs\" feature or select a different chip."); | 205 | compile_error!("USB_OTG_HS peripheral is not supported by this chip."); |
| 218 | } | 206 | } |
| 219 | } | 207 | } |
| 220 | } | 208 | } |
| @@ -222,12 +210,3 @@ foreach_peripheral!( | |||
| 222 | impl Instance for peripherals::$inst {} | 210 | impl Instance for peripherals::$inst {} |
| 223 | }; | 211 | }; |
| 224 | ); | 212 | ); |
| 225 | |||
| 226 | foreach_interrupt!( | ||
| 227 | ($inst:ident, otgfs, $block:ident, GLOBAL, $irq:ident) => { | ||
| 228 | unsafe impl USBInterrupt for crate::interrupt::$irq {} | ||
| 229 | }; | ||
| 230 | ($inst:ident, otghs, $block:ident, GLOBAL, $irq:ident) => { | ||
| 231 | unsafe impl USBInterrupt for crate::interrupt::$irq {} | ||
| 232 | }; | ||
| 233 | ); | ||
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 1bc406495..e2065bed9 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml | |||
| @@ -8,7 +8,7 @@ resolver = "2" | |||
| 8 | 8 | ||
| 9 | [dependencies] | 9 | [dependencies] |
| 10 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } | 10 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "usb-otg"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 14 | defmt-rtt = "0.3" |
diff --git a/examples/stm32f4/src/bin/usb_uart.rs b/examples/stm32f4/src/bin/usb_uart.rs deleted file mode 100644 index 251ed1eb0..000000000 --- a/examples/stm32f4/src/bin/usb_uart.rs +++ /dev/null | |||
| @@ -1,99 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt_rtt as _; // global logger | ||
| 6 | use panic_probe as _; | ||
| 7 | |||
| 8 | use defmt::{info, unwrap}; | ||
| 9 | use defmt_rtt as _; // global logger | ||
| 10 | use embassy::interrupt::InterruptExt; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use panic_probe as _; // print out panic messages | ||
| 13 | |||
| 14 | use embassy::executor::Spawner; | ||
| 15 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 16 | use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial}; | ||
| 17 | use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals}; | ||
| 18 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 19 | |||
| 20 | static mut EP_MEMORY: [u32; 2048] = [0; 2048]; | ||
| 21 | |||
| 22 | // USB requires at least 48 MHz clock | ||
| 23 | fn config() -> Config { | ||
| 24 | let mut config = Config::default(); | ||
| 25 | config.rcc.sys_ck = Some(Hertz(48_000_000)); | ||
| 26 | config | ||
| 27 | } | ||
| 28 | |||
| 29 | #[embassy::main(config = "config()")] | ||
| 30 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 31 | let mut rx_buffer = [0u8; 64]; | ||
| 32 | // we send back input + cr + lf | ||
| 33 | let mut tx_buffer = [0u8; 66]; | ||
| 34 | |||
| 35 | let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11); | ||
| 36 | let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY }); | ||
| 37 | |||
| 38 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 39 | |||
| 40 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 41 | .manufacturer("Fake company") | ||
| 42 | .product("Serial port") | ||
| 43 | .serial_number("TEST") | ||
| 44 | .device_class(0x02) | ||
| 45 | .build(); | ||
| 46 | |||
| 47 | let irq = interrupt::take!(OTG_FS); | ||
| 48 | irq.set_priority(interrupt::Priority::P3); | ||
| 49 | |||
| 50 | let mut state = State::new(); | ||
| 51 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 52 | pin_mut!(usb); | ||
| 53 | |||
| 54 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 55 | |||
| 56 | info!("usb initialized!"); | ||
| 57 | |||
| 58 | unwrap!( | ||
| 59 | writer | ||
| 60 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 61 | .await | ||
| 62 | ); | ||
| 63 | |||
| 64 | let mut buf = [0u8; 64]; | ||
| 65 | loop { | ||
| 66 | let mut n = 0; | ||
| 67 | |||
| 68 | async { | ||
| 69 | loop { | ||
| 70 | let char = unwrap!(reader.read_byte().await); | ||
| 71 | |||
| 72 | if char == b'\r' || char == b'\n' { | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | |||
| 76 | buf[n] = char; | ||
| 77 | n += 1; | ||
| 78 | |||
| 79 | // stop if we're out of room | ||
| 80 | if n == buf.len() { | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | .await; | ||
| 86 | |||
| 87 | if n > 0 { | ||
| 88 | for char in buf[..n].iter_mut() { | ||
| 89 | // upper case | ||
| 90 | if 0x61 <= *char && *char <= 0x7a { | ||
| 91 | *char &= !0x20; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 95 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 96 | unwrap!(writer.flush().await); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
diff --git a/examples/stm32f4/src/bin/usb_uart_ulpi.rs b/examples/stm32f4/src/bin/usb_uart_ulpi.rs deleted file mode 100644 index f6c373602..000000000 --- a/examples/stm32f4/src/bin/usb_uart_ulpi.rs +++ /dev/null | |||
| @@ -1,114 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt_rtt as _; // global logger | ||
| 6 | use panic_probe as _; | ||
| 7 | |||
| 8 | use defmt::{info, unwrap}; | ||
| 9 | use defmt_rtt as _; // global logger | ||
| 10 | use embassy::interrupt::InterruptExt; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use panic_probe as _; // print out panic messages | ||
| 13 | |||
| 14 | use embassy::executor::Spawner; | ||
| 15 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 16 | use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial}; | ||
| 17 | use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals}; | ||
| 18 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 19 | |||
| 20 | static mut EP_MEMORY: [u32; 2048] = [0; 2048]; | ||
| 21 | |||
| 22 | // USB requires at least 48 MHz clock | ||
| 23 | fn config() -> Config { | ||
| 24 | let mut config = Config::default(); | ||
| 25 | config.rcc.sys_ck = Some(Hertz(48_000_000)); | ||
| 26 | config | ||
| 27 | } | ||
| 28 | |||
| 29 | #[embassy::main(config = "config()")] | ||
| 30 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 31 | let mut rx_buffer = [0u8; 64]; | ||
| 32 | // we send back input + cr + lf | ||
| 33 | let mut tx_buffer = [0u8; 66]; | ||
| 34 | |||
| 35 | // USB with external high-speed PHY | ||
| 36 | let peri = UsbOtg::new_hs_ulpi( | ||
| 37 | p.USB_OTG_HS, | ||
| 38 | p.PA5, | ||
| 39 | p.PC2, | ||
| 40 | p.PC3, | ||
| 41 | p.PC0, | ||
| 42 | p.PA3, | ||
| 43 | p.PB0, | ||
| 44 | p.PB1, | ||
| 45 | p.PB10, | ||
| 46 | p.PB11, | ||
| 47 | p.PB12, | ||
| 48 | p.PB13, | ||
| 49 | p.PB5, | ||
| 50 | ); | ||
| 51 | let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY }); | ||
| 52 | |||
| 53 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 54 | |||
| 55 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 56 | .manufacturer("Fake company") | ||
| 57 | .product("Serial port") | ||
| 58 | .serial_number("TEST") | ||
| 59 | .device_class(0x02) | ||
| 60 | .build(); | ||
| 61 | |||
| 62 | let irq = interrupt::take!(OTG_FS); | ||
| 63 | irq.set_priority(interrupt::Priority::P3); | ||
| 64 | |||
| 65 | let mut state = State::new(); | ||
| 66 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 67 | pin_mut!(usb); | ||
| 68 | |||
| 69 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 70 | |||
| 71 | info!("usb initialized!"); | ||
| 72 | |||
| 73 | unwrap!( | ||
| 74 | writer | ||
| 75 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 76 | .await | ||
| 77 | ); | ||
| 78 | |||
| 79 | let mut buf = [0u8; 64]; | ||
| 80 | loop { | ||
| 81 | let mut n = 0; | ||
| 82 | |||
| 83 | async { | ||
| 84 | loop { | ||
| 85 | let char = unwrap!(reader.read_byte().await); | ||
| 86 | |||
| 87 | if char == b'\r' || char == b'\n' { | ||
| 88 | break; | ||
| 89 | } | ||
| 90 | |||
| 91 | buf[n] = char; | ||
| 92 | n += 1; | ||
| 93 | |||
| 94 | // stop if we're out of room | ||
| 95 | if n == buf.len() { | ||
| 96 | break; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | .await; | ||
| 101 | |||
| 102 | if n > 0 { | ||
| 103 | for char in buf[..n].iter_mut() { | ||
| 104 | // upper case | ||
| 105 | if 0x61 <= *char && *char <= 0x7a { | ||
| 106 | *char &= !0x20; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 110 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 111 | unwrap!(writer.flush().await); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 2da549bb2..b3478f74e 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml | |||
| @@ -10,7 +10,7 @@ resolver = "2" | |||
| 10 | [dependencies] | 10 | [dependencies] |
| 11 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-traits = { version = "0.1.0", path = "../../embassy-traits" } | 12 | embassy-traits = { version = "0.1.0", path = "../../embassy-traits" } |
| 13 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "usb-otg"] } | 13 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } |
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "0.3" |
| 16 | defmt-rtt = "0.3" | 16 | defmt-rtt = "0.3" |
diff --git a/examples/stm32l4/src/bin/usb_uart.rs b/examples/stm32l4/src/bin/usb_uart.rs deleted file mode 100644 index 878309550..000000000 --- a/examples/stm32l4/src/bin/usb_uart.rs +++ /dev/null | |||
| @@ -1,115 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt_rtt as _; // global logger | ||
| 6 | use panic_probe as _; | ||
| 7 | |||
| 8 | use defmt::{info, unwrap}; | ||
| 9 | use defmt_rtt as _; // global logger | ||
| 10 | use embassy::interrupt::InterruptExt; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use panic_probe as _; // print out panic messages | ||
| 13 | |||
| 14 | use embassy::executor::Spawner; | ||
| 15 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 16 | use embassy_stm32::pac::pwr::vals::Usv; | ||
| 17 | use embassy_stm32::pac::{PWR, RCC}; | ||
| 18 | use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; | ||
| 19 | use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial}; | ||
| 20 | use embassy_stm32::{interrupt, Config, Peripherals}; | ||
| 21 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 22 | |||
| 23 | static mut EP_MEMORY: [u32; 2048] = [0; 2048]; | ||
| 24 | |||
| 25 | // USB requires at least 48 MHz clock | ||
| 26 | fn config() -> Config { | ||
| 27 | let mut config = Config::default(); | ||
| 28 | // set up a 80Mhz clock | ||
| 29 | config.rcc.mux = ClockSrc::PLL( | ||
| 30 | PLLSource::HSI16, | ||
| 31 | PLLClkDiv::Div2, | ||
| 32 | PLLSrcDiv::Div2, | ||
| 33 | PLLMul::Mul20, | ||
| 34 | None, | ||
| 35 | ); | ||
| 36 | // enable HSI48 clock for USB | ||
| 37 | config.rcc.hsi48 = true; | ||
| 38 | config | ||
| 39 | } | ||
| 40 | |||
| 41 | #[embassy::main(config = "config()")] | ||
| 42 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 43 | // Enable PWR peripheral | ||
| 44 | unsafe { RCC.apb1enr1().modify(|w| w.set_pwren(true)) }; | ||
| 45 | unsafe { PWR.cr2().modify(|w| w.set_usv(Usv::VALID)) } | ||
| 46 | |||
| 47 | let mut rx_buffer = [0u8; 64]; | ||
| 48 | // we send back input + cr + lf | ||
| 49 | let mut tx_buffer = [0u8; 66]; | ||
| 50 | |||
| 51 | let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11); | ||
| 52 | let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY }); | ||
| 53 | |||
| 54 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 55 | |||
| 56 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 57 | .manufacturer("Fake company") | ||
| 58 | .product("Serial port") | ||
| 59 | .serial_number("TEST") | ||
| 60 | .device_class(0x02) | ||
| 61 | .build(); | ||
| 62 | |||
| 63 | let irq = interrupt::take!(OTG_FS); | ||
| 64 | irq.set_priority(interrupt::Priority::P3); | ||
| 65 | |||
| 66 | let mut state = State::new(); | ||
| 67 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 68 | pin_mut!(usb); | ||
| 69 | |||
| 70 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 71 | |||
| 72 | info!("usb initialized!"); | ||
| 73 | |||
| 74 | unwrap!( | ||
| 75 | writer | ||
| 76 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 77 | .await | ||
| 78 | ); | ||
| 79 | |||
| 80 | let mut buf = [0u8; 64]; | ||
| 81 | loop { | ||
| 82 | let mut n = 0; | ||
| 83 | |||
| 84 | async { | ||
| 85 | loop { | ||
| 86 | let char = unwrap!(reader.read_byte().await); | ||
| 87 | |||
| 88 | if char == b'\r' || char == b'\n' { | ||
| 89 | break; | ||
| 90 | } | ||
| 91 | |||
| 92 | buf[n] = char; | ||
| 93 | n += 1; | ||
| 94 | |||
| 95 | // stop if we're out of room | ||
| 96 | if n == buf.len() { | ||
| 97 | break; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | .await; | ||
| 102 | |||
| 103 | if n > 0 { | ||
| 104 | for char in buf[..n].iter_mut() { | ||
| 105 | // upper case | ||
| 106 | if 0x61 <= *char && *char <= 0x7a { | ||
| 107 | *char &= !0x20; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 111 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 112 | unwrap!(writer.flush().await); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
