aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-05-04 01:00:38 +0200
committerDario Nieuwenhuis <[email protected]>2022-05-04 01:41:37 +0200
commitfc32b3750c448a81b7dd44cf9de98723b8eb4fcf (patch)
tree989562f5750d28ab11732633839db8f25a1ad773
parent85c0525e01c52bbb85c7b93600a60837ee7b87dc (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.toml1
-rw-r--r--embassy-hal-common/src/lib.rs1
-rw-r--r--embassy-hal-common/src/usb/cdc_acm.rs338
-rw-r--r--embassy-hal-common/src/usb/mod.rs267
-rw-r--r--embassy-hal-common/src/usb/usb_serial.rs345
-rw-r--r--embassy-stm32/Cargo.toml2
-rw-r--r--embassy-stm32/build.rs32
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/usb_otg.rs61
-rw-r--r--examples/stm32f4/Cargo.toml2
-rw-r--r--examples/stm32f4/src/bin/usb_uart.rs99
-rw-r--r--examples/stm32f4/src/bin/usb_uart_ulpi.rs114
-rw-r--r--examples/stm32l4/Cargo.toml2
-rw-r--r--examples/stm32l4/src/bin/usb_uart.rs115
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" }
12defmt = { version = "0.3", optional = true } 12defmt = { version = "0.3", optional = true }
13log = { version = "0.4.14", optional = true } 13log = { version = "0.4.14", optional = true }
14cortex-m = "0.7.3" 14cortex-m = "0.7.3"
15usb-device = "0.2.8"
16num-traits = { version = "0.2.14", default-features = false } 15num-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;
10pub mod peripheral; 10pub mod peripheral;
11pub mod ratio; 11pub mod ratio;
12pub mod ring_buffer; 12pub mod ring_buffer;
13pub mod usb;
14 13
15/// Low power blocking wait loop using WFE/SEV. 14/// Low power blocking wait loop using WFE/SEV.
16pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) { 15pub 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
4use core::convert::TryInto;
5use core::mem;
6use usb_device::class_prelude::*;
7use usb_device::Result;
8
9/// This should be used as `device_class` when building the `UsbDevice`.
10pub const USB_CLASS_CDC: u8 = 0x02;
11
12const USB_CLASS_CDC_DATA: u8 = 0x0a;
13const CDC_SUBCLASS_ACM: u8 = 0x02;
14const CDC_PROTOCOL_NONE: u8 = 0x00;
15
16const CS_INTERFACE: u8 = 0x24;
17const CDC_TYPE_HEADER: u8 = 0x00;
18const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01;
19const CDC_TYPE_ACM: u8 = 0x02;
20const CDC_TYPE_UNION: u8 = 0x06;
21
22const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
23#[allow(unused)]
24const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
25const REQ_SET_LINE_CODING: u8 = 0x20;
26const REQ_GET_LINE_CODING: u8 = 0x21;
27const 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.
43pub 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
54impl<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
118impl<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)]
255pub 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
266impl 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)]
278pub enum ParityType {
279 None = 0,
280 Odd = 1,
281 Event = 2,
282 Mark = 3,
283 Space = 4,
284}
285
286impl 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.
300pub struct LineCoding {
301 stop_bits: StopBits,
302 data_bits: u8,
303 parity_type: ParityType,
304 data_rate: u32,
305}
306
307impl 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
329impl 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 @@
1use core::cell::RefCell;
2use core::marker::PhantomData;
3use core::pin::Pin;
4
5use usb_device::bus::UsbBus;
6use usb_device::class::UsbClass;
7use usb_device::device::UsbDevice;
8
9mod cdc_acm;
10pub mod usb_serial;
11
12use crate::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
13use embassy::interrupt::Interrupt;
14pub use usb_serial::{ReadInterface, UsbSerial, WriteInterface};
15
16/// Marker trait to mark an interrupt to be used with the [`Usb`] abstraction.
17pub unsafe trait USBInterrupt: Interrupt + Send {}
18
19pub struct State<'bus, B, T, I>(StateStorage<StateInner<'bus, B, T, I>>)
20where
21 B: UsbBus,
22 T: ClassSet<B>,
23 I: USBInterrupt;
24
25impl<'bus, B, T, I> State<'bus, B, T, I>
26where
27 B: UsbBus,
28 T: ClassSet<B>,
29 I: USBInterrupt,
30{
31 pub fn new() -> Self {
32 Self(StateStorage::new())
33 }
34}
35
36pub(crate) struct StateInner<'bus, B, T, I>
37where
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
47pub struct Usb<'bus, B, T, I>
48where
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
57impl<'bus, B, T, I> Usb<'bus, B, T, I>
58where
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
81impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I>
82where
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
111impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I>
112where
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
141impl<'bus, B, T, I> PeripheralState for StateInner<'bus, B, T, I>
142where
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
153pub trait ClassSet<B: UsbBus>: Send {
154 fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool;
155}
156
157pub trait IntoClassSet<B: UsbBus, C: ClassSet<B>> {
158 fn into_class_set(self) -> C;
159}
160
161pub struct ClassSet1<B, C1>
162where
163 B: UsbBus,
164 C1: UsbClass<B>,
165{
166 class: C1,
167 _bus: PhantomData<B>,
168}
169
170pub struct ClassSet2<B, C1, C2>
171where
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`]
182pub struct Index0;
183
184/// The second class into a [`ClassSet`]
185pub struct Index1;
186
187impl<B, C1> ClassSet<B> for ClassSet1<B, C1>
188where
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
197impl<B, C1, C2> ClassSet<B> for ClassSet2<B, C1, C2>
198where
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
208impl<B, C1> IntoClassSet<B, ClassSet1<B, C1>> for C1
209where
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
221impl<B, C1, C2> IntoClassSet<B, ClassSet2<B, C1, C2>> for (C1, C2)
222where
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
237pub trait SerialState<'bus, 'a, B: UsbBus, I> {
238 fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B>;
239}
240
241impl<'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
249impl<'bus, 'a, B, C2> SerialState<'bus, 'a, B, Index0> for ClassSet2<B, UsbSerial<'bus, 'a, B>, C2>
250where
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
259impl<'bus, 'a, B, C1> SerialState<'bus, 'a, B, Index1> for ClassSet2<B, C1, UsbSerial<'bus, 'a, B>>
260where
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 @@
1use core::cell::RefCell;
2use core::marker::{PhantomData, Unpin};
3use core::pin::Pin;
4use core::task::{Context, Poll};
5
6use embassy::io::{self, AsyncBufRead, AsyncWrite};
7use embassy::waitqueue::WakerRegistration;
8use usb_device::bus::UsbBus;
9use usb_device::class_prelude::*;
10use usb_device::UsbError;
11
12use super::cdc_acm::CdcAcmClass;
13use super::StateInner;
14use crate::peripheral::PeripheralMutex;
15use crate::ring_buffer::RingBuffer;
16use crate::usb::{ClassSet, SerialState, USBInterrupt};
17
18pub struct ReadInterface<'a, 'bus, 'c, I, B, T, INT>
19where
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
35pub struct WriteInterface<'a, 'bus, 'c, I, B, T, INT>
36where
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
48impl<'a, 'bus, 'c, I, B, T, INT> AsyncBufRead for ReadInterface<'a, 'bus, 'c, I, B, T, INT>
49where
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
88impl<'a, 'bus, 'c, I, B, T, INT> AsyncWrite for WriteInterface<'a, 'bus, 'c, I, B, T, INT>
89where
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
122pub 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
133impl<'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
155impl<'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.
189enum 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
203impl<'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
292impl<B> UsbClass<B> for UsbSerial<'_, '_, B>
293where
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
53rand_core = "0.6.3" 53rand_core = "0.6.3"
54sdio-host = "0.5.0" 54sdio-host = "0.5.0"
55embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true } 55embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true }
56synopsys-usb-otg = { version = "0.3", features = ["cortex-m", "hs"], optional = true }
57critical-section = "0.2.5" 56critical-section = "0.2.5"
58bare-metal = "1.0.0" 57bare-metal = "1.0.0"
59atomic-polyfill = "0.1.5" 58atomic-polyfill = "0.1.5"
@@ -76,7 +75,6 @@ net = ["embassy-net", "vcell"]
76memory-x = ["stm32-metapac/memory-x"] 75memory-x = ["stm32-metapac/memory-x"]
77subghz = [] 76subghz = []
78exti = [] 77exti = []
79usb-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;
61pub mod spi; 61pub mod spi;
62#[cfg(usart)] 62#[cfg(usart)]
63pub mod usart; 63pub mod usart;
64#[cfg(feature = "usb-otg")] 64#[cfg(any(otgfs, otghs))]
65pub mod usb_otg; 65pub 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 @@
1use core::marker::PhantomData; 1use core::marker::PhantomData;
2use embassy::util::Unborrow; 2use embassy::util::Unborrow;
3use embassy_hal_common::unborrow; 3use embassy_hal_common::unborrow;
4use synopsys_usb_otg::{PhyType, UsbPeripheral};
5 4
6use crate::gpio::sealed::AFType; 5use crate::gpio::sealed::AFType;
7use crate::gpio::Speed; 6use crate::gpio::Speed;
8use crate::{peripherals, rcc::RccPeripheral}; 7use crate::{peripherals, rcc::RccPeripheral};
9 8
10pub use embassy_hal_common::usb::*;
11pub use synopsys_usb_otg::UsbBus;
12
13macro_rules! config_ulpi_pins { 9macro_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)]
24pub 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
26pub struct UsbOtg<'d, T: Instance> { 37pub 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
31impl<'d, T: Instance> UsbOtg<'d, T> { 42impl<'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
86unsafe impl<'d, T: Instance> Send for UsbOtg<'d, T> {}
87unsafe impl<'d, T: Instance> Sync for UsbOtg<'d, T> {}
88
89unsafe 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
109pub(crate) mod sealed { 97pub(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
226foreach_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]
10embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } 10embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "usb-otg"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
12 12
13defmt = "0.3" 13defmt = "0.3"
14defmt-rtt = "0.3" 14defmt-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
5use defmt_rtt as _; // global logger
6use panic_probe as _;
7
8use defmt::{info, unwrap};
9use defmt_rtt as _; // global logger
10use embassy::interrupt::InterruptExt;
11use futures::pin_mut;
12use panic_probe as _; // print out panic messages
13
14use embassy::executor::Spawner;
15use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
16use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
17use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals};
18use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
19
20static mut EP_MEMORY: [u32; 2048] = [0; 2048];
21
22// USB requires at least 48 MHz clock
23fn 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()")]
30async 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
5use defmt_rtt as _; // global logger
6use panic_probe as _;
7
8use defmt::{info, unwrap};
9use defmt_rtt as _; // global logger
10use embassy::interrupt::InterruptExt;
11use futures::pin_mut;
12use panic_probe as _; // print out panic messages
13
14use embassy::executor::Spawner;
15use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
16use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
17use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals};
18use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
19
20static mut EP_MEMORY: [u32; 2048] = [0; 2048];
21
22// USB requires at least 48 MHz clock
23fn 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()")]
30async 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]
11embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-traits = { version = "0.1.0", path = "../../embassy-traits" } 12embassy-traits = { version = "0.1.0", path = "../../embassy-traits" }
13embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "usb-otg"] } 13embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] }
14 14
15defmt = "0.3" 15defmt = "0.3"
16defmt-rtt = "0.3" 16defmt-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
5use defmt_rtt as _; // global logger
6use panic_probe as _;
7
8use defmt::{info, unwrap};
9use defmt_rtt as _; // global logger
10use embassy::interrupt::InterruptExt;
11use futures::pin_mut;
12use panic_probe as _; // print out panic messages
13
14use embassy::executor::Spawner;
15use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
16use embassy_stm32::pac::pwr::vals::Usv;
17use embassy_stm32::pac::{PWR, RCC};
18use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
19use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
20use embassy_stm32::{interrupt, Config, Peripherals};
21use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
22
23static mut EP_MEMORY: [u32; 2048] = [0; 2048];
24
25// USB requires at least 48 MHz clock
26fn 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()")]
42async 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}