aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThales Fragoso <[email protected]>2021-02-18 21:57:35 -0300
committerThales Fragoso <[email protected]>2021-03-19 19:44:08 -0300
commit890e93b4f009afe37b35e211567acfbf58be783a (patch)
treec88b2fa578721fe25e0f0295c8d9a24c72e2ef40
parent6719da3b6e726608fcde1ebdd27eb70ac9bb6d69 (diff)
Start working on usb serial
-rw-r--r--embassy-stm32f4-examples/Cargo.toml5
-rw-r--r--embassy-stm32f4-examples/src/bin/usb_serial.rs119
-rw-r--r--embassy-stm32f4/Cargo.toml1
-rw-r--r--embassy-stm32f4/src/cdc_acm.rs338
-rw-r--r--embassy-stm32f4/src/lib.rs7
-rw-r--r--embassy-stm32f4/src/usb.rs130
-rw-r--r--embassy-stm32f4/src/usb_serial.rs290
-rw-r--r--embassy-stm32f4/src/util/mod.rs12
-rw-r--r--embassy-stm32f4/src/util/peripheral.rs78
-rw-r--r--embassy-stm32f4/src/util/ring_buffer.rs86
10 files changed, 1064 insertions, 2 deletions
diff --git a/embassy-stm32f4-examples/Cargo.toml b/embassy-stm32f4-examples/Cargo.toml
index 5bbaecc58..e4f2aa7a1 100644
--- a/embassy-stm32f4-examples/Cargo.toml
+++ b/embassy-stm32f4-examples/Cargo.toml
@@ -46,7 +46,8 @@ cortex-m = "0.7.1"
46cortex-m-rt = "0.6.13" 46cortex-m-rt = "0.6.13"
47embedded-hal = { version = "0.2.4" } 47embedded-hal = { version = "0.2.4" }
48panic-probe = "0.1.0" 48panic-probe = "0.1.0"
49stm32f4xx-hal = { version = "0.8.3", features = ["rt"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"} 49stm32f4xx-hal = { version = "0.8.3", features = ["rt", "usb_fs"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"}
50futures = { version = "0.3.8", default-features = false, features = ["async-await"] } 50futures = { version = "0.3.8", default-features = false, features = ["async-await"] }
51rtt-target = { version = "0.3", features = ["cortex-m"] } 51rtt-target = { version = "0.3", features = ["cortex-m"] }
52bxcan = "0.5.0" \ No newline at end of file 52bxcan = "0.5.0"
53usb-device = "0.2.7"
diff --git a/embassy-stm32f4-examples/src/bin/usb_serial.rs b/embassy-stm32f4-examples/src/bin/usb_serial.rs
new file mode 100644
index 000000000..d2ccb4b21
--- /dev/null
+++ b/embassy-stm32f4-examples/src/bin/usb_serial.rs
@@ -0,0 +1,119 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7use example_common::*;
8
9use cortex_m_rt::entry;
10use defmt::panic;
11use embassy::executor::{task, Executor};
12use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
13use embassy::time::{Duration, Timer};
14use embassy::util::Forever;
15use embassy_stm32f4::interrupt::OwnedInterrupt;
16use embassy_stm32f4::usb::Usb;
17use embassy_stm32f4::usb_serial::UsbSerial;
18use embassy_stm32f4::{interrupt, pac, rtc};
19use futures::future::{select, Either};
20use futures::pin_mut;
21use stm32f4xx_hal::otg_fs::{UsbBus, USB};
22use stm32f4xx_hal::prelude::*;
23use usb_device::bus::UsbBusAllocator;
24use usb_device::prelude::*;
25
26#[task]
27async fn run1(bus: &'static mut UsbBusAllocator<UsbBus<USB>>) {
28 info!("Async task");
29
30 let mut read_buf = [0u8; 128];
31 let mut write_buf = [0u8; 128];
32 let serial = UsbSerial::new(bus, &mut read_buf, &mut write_buf);
33
34 let device = UsbDeviceBuilder::new(bus, UsbVidPid(0x16c0, 0x27dd))
35 .manufacturer("Fake company")
36 .product("Serial port")
37 .serial_number("TEST")
38 .device_class(0x02)
39 .build();
40
41 let irq = interrupt::take!(OTG_FS);
42 irq.set_priority(interrupt::Priority::Level3);
43
44 let usb = Usb::new(device, serial, irq);
45 pin_mut!(usb);
46
47 let (mut read_interface, mut write_interface) = usb.as_mut().into_ref().take_serial();
48
49 let mut buf = [0u8; 5];
50 loop {
51 let recv_fut = read_interface.read(&mut buf);
52 let timeout = Timer::after(Duration::from_ticks(32768 * 3));
53
54 match select(recv_fut, timeout).await {
55 Either::Left((recv, _)) => {
56 let recv = unwrap!(recv);
57 unwrap!(write_interface.write_all(&buf[..recv]).await);
58 }
59 Either::Right(_) => {
60 unwrap!(write_interface.write_all(b"Hello\r\n").await);
61 }
62 }
63 }
64}
65
66static RTC: Forever<rtc::RTC<pac::TIM2>> = Forever::new();
67static ALARM: Forever<rtc::Alarm<pac::TIM2>> = Forever::new();
68static EXECUTOR: Forever<Executor> = Forever::new();
69static USB_BUS: Forever<UsbBusAllocator<UsbBus<USB>>> = Forever::new();
70
71#[entry]
72fn main() -> ! {
73 static mut EP_MEMORY: [u32; 1024] = [0; 1024];
74
75 info!("Hello World!");
76
77 let p = unwrap!(pac::Peripherals::take());
78
79 p.RCC.ahb1enr.modify(|_, w| w.dma1en().enabled());
80 let rcc = p.RCC.constrain();
81 let clocks = rcc
82 .cfgr
83 .use_hse(25.mhz())
84 .sysclk(48.mhz())
85 .require_pll48clk()
86 .freeze();
87
88 p.DBGMCU.cr.modify(|_, w| {
89 w.dbg_sleep().set_bit();
90 w.dbg_standby().set_bit();
91 w.dbg_stop().set_bit()
92 });
93
94 let rtc = RTC.put(rtc::RTC::new(p.TIM2, interrupt::take!(TIM2), clocks));
95 rtc.start();
96
97 unsafe { embassy::time::set_clock(rtc) };
98
99 let alarm = ALARM.put(rtc.alarm1());
100 let executor = EXECUTOR.put(Executor::new());
101 executor.set_alarm(alarm);
102
103 let gpioa = p.GPIOA.split();
104 let usb = USB {
105 usb_global: p.OTG_FS_GLOBAL,
106 usb_device: p.OTG_FS_DEVICE,
107 usb_pwrclk: p.OTG_FS_PWRCLK,
108 pin_dm: gpioa.pa11.into_alternate_af10(),
109 pin_dp: gpioa.pa12.into_alternate_af10(),
110 hclk: clocks.hclk(),
111 };
112 // Rust analyzer isn't recognizing the static ref magic `cortex-m` does
113 #[allow(unused_unsafe)]
114 let usb_bus = USB_BUS.put(UsbBus::new(usb, unsafe { EP_MEMORY }));
115
116 executor.run(move |spawner| {
117 unwrap!(spawner.spawn(run1(usb_bus)));
118 });
119}
diff --git a/embassy-stm32f4/Cargo.toml b/embassy-stm32f4/Cargo.toml
index ae3273d67..b39a141b4 100644
--- a/embassy-stm32f4/Cargo.toml
+++ b/embassy-stm32f4/Cargo.toml
@@ -41,3 +41,4 @@ embedded-dma = { version = "0.1.2" }
41stm32f4xx-hal = { version = "0.8.3", features = ["rt", "can"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"} 41stm32f4xx-hal = { version = "0.8.3", features = ["rt", "can"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"}
42bxcan = "0.5.0" 42bxcan = "0.5.0"
43nb = "*" 43nb = "*"
44usb-device = "0.2.7"
diff --git a/embassy-stm32f4/src/cdc_acm.rs b/embassy-stm32f4/src/cdc_acm.rs
new file mode 100644
index 000000000..5a85b3846
--- /dev/null
+++ b/embassy-stm32f4/src/cdc_acm.rs
@@ -0,0 +1,338 @@
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-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs
index 0d490525c..1788f5e77 100644
--- a/embassy-stm32f4/src/lib.rs
+++ b/embassy-stm32f4/src/lib.rs
@@ -316,3 +316,10 @@ pub mod exti;
316pub mod qei; 316pub mod qei;
317pub mod rtc; 317pub mod rtc;
318pub mod serial; 318pub mod serial;
319pub mod usb;
320pub mod usb_serial;
321pub mod util;
322
323pub(crate) mod cdc_acm;
324
325pub use cortex_m_rt::interrupt;
diff --git a/embassy-stm32f4/src/usb.rs b/embassy-stm32f4/src/usb.rs
new file mode 100644
index 000000000..613b9ecb7
--- /dev/null
+++ b/embassy-stm32f4/src/usb.rs
@@ -0,0 +1,130 @@
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
9use crate::interrupt;
10use crate::usb_serial::{ReadInterface, UsbSerial, WriteInterface};
11use crate::util::peripheral::{PeripheralMutex, PeripheralState};
12
13pub struct State<'bus, B: UsbBus, T: ClassSet<B>> {
14 device: UsbDevice<'bus, B>,
15 pub(crate) classes: T,
16}
17
18pub struct Usb<'bus, B: UsbBus, T: ClassSet<B>> {
19 // Don't you dare moving out `PeripheralMutex`
20 inner: RefCell<PeripheralMutex<State<'bus, B, T>>>,
21}
22
23impl<'bus, B, T> Usb<'bus, B, T>
24where
25 B: UsbBus,
26 T: ClassSet<B>,
27{
28 pub fn new<S: IntoClassSet<B, T>>(
29 device: UsbDevice<'bus, B>,
30 class_set: S,
31 irq: interrupt::OTG_FSInterrupt,
32 ) -> Self {
33 let state = State {
34 device,
35 classes: class_set.into_class_set(),
36 };
37 let mutex = PeripheralMutex::new(state, irq);
38 Self {
39 inner: RefCell::new(mutex),
40 }
41 }
42
43 pub fn start(self: Pin<&mut Self>) {
44 let this = unsafe { self.get_unchecked_mut() };
45 let mut mutex = this.inner.borrow_mut();
46 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) };
47
48 // Use inner to register the irq
49 mutex.with(|_, _| {});
50 }
51}
52
53impl<'bus, 'c, B, T> Usb<'bus, B, T>
54where
55 B: UsbBus,
56 T: ClassSet<B> + SerialState<'bus, 'c, B>,
57{
58 pub fn take_serial<'a>(
59 self: Pin<&'a Self>,
60 ) -> (
61 ReadInterface<'a, 'bus, 'c, B, T>,
62 WriteInterface<'a, 'bus, 'c, B, T>,
63 ) {
64 let this = self.get_ref();
65
66 let r = ReadInterface {
67 inner: &this.inner,
68 _buf_lifetime: PhantomData,
69 };
70
71 let w = WriteInterface {
72 inner: &this.inner,
73 _buf_lifetime: PhantomData,
74 };
75 (r, w)
76 }
77}
78
79impl<'bus, B, T> PeripheralState for State<'bus, B, T>
80where
81 B: UsbBus,
82 T: ClassSet<B>,
83{
84 type Interrupt = interrupt::OTG_FSInterrupt;
85 fn on_interrupt(&mut self) {
86 self.classes.poll_all(&mut self.device);
87 }
88}
89
90pub trait ClassSet<B: UsbBus> {
91 fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool;
92}
93
94pub trait IntoClassSet<B: UsbBus, C: ClassSet<B>> {
95 fn into_class_set(self) -> C;
96}
97
98pub struct ClassSet1<B: UsbBus, T: UsbClass<B>> {
99 class: T,
100 _bus: PhantomData<B>,
101}
102
103impl<B, T> ClassSet<B> for ClassSet1<B, T>
104where
105 B: UsbBus,
106 T: UsbClass<B>,
107{
108 fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
109 device.poll(&mut [&mut self.class])
110 }
111}
112
113impl<B: UsbBus, T: UsbClass<B>> IntoClassSet<B, ClassSet1<B, T>> for T {
114 fn into_class_set(self) -> ClassSet1<B, T> {
115 ClassSet1 {
116 class: self,
117 _bus: PhantomData,
118 }
119 }
120}
121
122pub trait SerialState<'bus, 'a, B: UsbBus> {
123 fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B>;
124}
125
126impl<'bus, 'a, B: UsbBus> SerialState<'bus, 'a, B> for ClassSet1<B, UsbSerial<'bus, 'a, B>> {
127 fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
128 &mut self.class
129 }
130}
diff --git a/embassy-stm32f4/src/usb_serial.rs b/embassy-stm32f4/src/usb_serial.rs
new file mode 100644
index 000000000..284d7e5f6
--- /dev/null
+++ b/embassy-stm32f4/src/usb_serial.rs
@@ -0,0 +1,290 @@
1use core::cell::RefCell;
2use core::marker::{PhantomData, PhantomPinned};
3use core::pin::Pin;
4use core::task::{Context, Poll};
5
6use embassy::io::{self, AsyncBufRead, AsyncWrite};
7use embassy::util::WakerRegistration;
8use usb_device::bus::UsbBus;
9use usb_device::class_prelude::*;
10use usb_device::UsbError;
11
12use crate::cdc_acm::CdcAcmClass;
13use crate::usb::{ClassSet, SerialState, State};
14use crate::util::peripheral::PeripheralMutex;
15use crate::util::ring_buffer::RingBuffer;
16
17pub struct ReadInterface<'a, 'bus, 'c, B: UsbBus, T: SerialState<'bus, 'c, B> + ClassSet<B>> {
18 // Don't you dare moving out `PeripheralMutex`
19 pub(crate) inner: &'a RefCell<PeripheralMutex<State<'bus, B, T>>>,
20 pub(crate) _buf_lifetime: PhantomData<&'c T>,
21}
22
23/// Write interface for USB CDC_ACM
24///
25/// This interface is buffered, meaning that after the write returns the bytes might not be fully
26/// on the wire just yet
27pub struct WriteInterface<'a, 'bus, 'c, B: UsbBus, T: SerialState<'bus, 'c, B> + ClassSet<B>> {
28 // Don't you dare moving out `PeripheralMutex`
29 pub(crate) inner: &'a RefCell<PeripheralMutex<State<'bus, B, T>>>,
30 pub(crate) _buf_lifetime: PhantomData<&'c T>,
31}
32
33impl<'a, 'bus, 'c, B, T> AsyncBufRead for ReadInterface<'a, 'bus, 'c, B, T>
34where
35 B: UsbBus,
36 T: SerialState<'bus, 'c, B> + ClassSet<B>,
37{
38 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
39 let this = self.get_mut();
40 let mut mutex = this.inner.borrow_mut();
41 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) };
42 mutex.with(|state, _irq| {
43 let serial = state.classes.get_serial();
44 let serial = Pin::new(serial);
45
46 match serial.poll_fill_buf(cx) {
47 Poll::Ready(Ok(buf)) => {
48 let buf: &[u8] = buf;
49 let buf: &[u8] = unsafe { core::mem::transmute(buf) };
50 Poll::Ready(Ok(buf))
51 }
52 Poll::Ready(Err(_)) => Poll::Ready(Err(io::Error::Other)),
53 Poll::Pending => Poll::Pending,
54 }
55 })
56 }
57
58 fn consume(self: Pin<&mut Self>, amt: usize) {
59 let this = self.get_mut();
60 let mut mutex = this.inner.borrow_mut();
61 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) };
62 mutex.with(|state, _irq| {
63 let serial = state.classes.get_serial();
64 let serial = Pin::new(serial);
65
66 serial.consume(amt);
67 })
68 }
69}
70
71impl<'a, 'bus, 'c, B, T> AsyncWrite for WriteInterface<'a, 'bus, 'c, B, T>
72where
73 B: UsbBus,
74 T: SerialState<'bus, 'c, B> + ClassSet<B>,
75{
76 fn poll_write(
77 self: Pin<&mut Self>,
78 cx: &mut Context<'_>,
79 buf: &[u8],
80 ) -> Poll<io::Result<usize>> {
81 let this = self.get_mut();
82 let mut mutex = this.inner.borrow_mut();
83 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) };
84 mutex.with(|state, _irq| {
85 let serial = state.classes.get_serial();
86 let serial = Pin::new(serial);
87
88 serial.poll_write(cx, buf)
89 })
90 }
91}
92
93pub struct UsbSerial<'bus, 'a, B: UsbBus> {
94 inner: CdcAcmClass<'bus, B>,
95 read_buf: RingBuffer<'a>,
96 write_buf: RingBuffer<'a>,
97 read_waker: WakerRegistration,
98 write_waker: WakerRegistration,
99 write_state: WriteState,
100 read_error: bool,
101 write_error: bool,
102}
103
104impl<'bus, 'a, B: UsbBus> AsyncBufRead for UsbSerial<'bus, 'a, B> {
105 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
106 let this = self.get_mut();
107
108 if this.read_error {
109 this.read_error = false;
110 return Poll::Ready(Err(io::Error::Other));
111 }
112
113 let buf = this.read_buf.pop_buf();
114 if buf.is_empty() {
115 this.read_waker.register(cx.waker());
116 return Poll::Pending;
117 }
118 Poll::Ready(Ok(buf))
119 }
120
121 fn consume(self: Pin<&mut Self>, amt: usize) {
122 self.get_mut().read_buf.pop(amt);
123 }
124}
125
126impl<'bus, 'a, B: UsbBus> AsyncWrite for UsbSerial<'bus, 'a, B> {
127 fn poll_write(
128 self: Pin<&mut Self>,
129 cx: &mut Context<'_>,
130 buf: &[u8],
131 ) -> Poll<io::Result<usize>> {
132 let this = self.get_mut();
133
134 if this.write_error {
135 this.write_error = false;
136 return Poll::Ready(Err(io::Error::Other));
137 }
138
139 let write_buf = this.write_buf.push_buf();
140 if write_buf.is_empty() {
141 this.write_waker.register(cx.waker());
142 return Poll::Pending;
143 }
144
145 let count = write_buf.len().min(buf.len());
146 write_buf[..count].copy_from_slice(&buf[..count]);
147 this.write_buf.push(count);
148
149 this.flush_write();
150 Poll::Ready(Ok(count))
151 }
152}
153
154/// Keeps track of the type of the last written packet.
155enum WriteState {
156 /// No packets in-flight
157 Idle,
158
159 /// Short packet currently in-flight
160 Short,
161
162 /// Full packet current in-flight. A full packet must be followed by a short packet for the host
163 /// OS to see the transaction. The data is the number of subsequent full packets sent so far. A
164 /// short packet is forced every SHORT_PACKET_INTERVAL packets so that the OS sees data in a
165 /// timely manner.
166 Full(usize),
167}
168
169impl<'bus, 'a, B: UsbBus> UsbSerial<'bus, 'a, B> {
170 pub fn new(
171 alloc: &'bus UsbBusAllocator<B>,
172 read_buf: &'a mut [u8],
173 write_buf: &'a mut [u8],
174 ) -> Self {
175 Self {
176 inner: CdcAcmClass::new(alloc, 64),
177 read_buf: RingBuffer::new(read_buf),
178 write_buf: RingBuffer::new(write_buf),
179 read_waker: WakerRegistration::new(),
180 write_waker: WakerRegistration::new(),
181 write_state: WriteState::Idle,
182 read_error: false,
183 write_error: false,
184 }
185 }
186
187 fn flush_write(&mut self) {
188 /// If this many full size packets have been sent in a row, a short packet will be sent so that the
189 /// host sees the data in a timely manner.
190 const SHORT_PACKET_INTERVAL: usize = 10;
191
192 let full_size_packets = match self.write_state {
193 WriteState::Full(c) => c,
194 _ => 0,
195 };
196
197 let ep_size = self.inner.max_packet_size() as usize;
198 let max_size = if full_size_packets > SHORT_PACKET_INTERVAL {
199 ep_size - 1
200 } else {
201 ep_size
202 };
203
204 let buf = {
205 let buf = self.write_buf.pop_buf();
206 if buf.len() > max_size {
207 &buf[..max_size]
208 } else {
209 buf
210 }
211 };
212
213 if !buf.is_empty() {
214 let count = match self.inner.write_packet(buf) {
215 Ok(c) => c,
216 Err(UsbError::WouldBlock) => 0,
217 Err(_) => {
218 self.write_error = true;
219 return;
220 }
221 };
222
223 if buf.len() == ep_size {
224 self.write_state = WriteState::Full(full_size_packets + 1);
225 } else {
226 self.write_state = WriteState::Short;
227 }
228 self.write_buf.pop(count);
229 } else if full_size_packets > 0 {
230 if let Err(e) = self.inner.write_packet(&[]) {
231 if !matches!(e, UsbError::WouldBlock) {
232 self.write_error = true;
233 }
234 return;
235 }
236 self.write_state = WriteState::Idle;
237 }
238 }
239}
240
241impl<B> UsbClass<B> for UsbSerial<'_, '_, B>
242where
243 B: UsbBus,
244{
245 fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<(), UsbError> {
246 self.inner.get_configuration_descriptors(writer)
247 }
248
249 fn reset(&mut self) {
250 self.inner.reset();
251 self.read_buf.clear();
252 self.write_buf.clear();
253 self.write_state = WriteState::Idle;
254 }
255
256 fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
257 if addr == self.inner.write_ep_address() {
258 self.write_waker.wake();
259
260 self.flush_write();
261 }
262 }
263
264 fn endpoint_out(&mut self, addr: EndpointAddress) {
265 if addr == self.inner.read_ep_address() {
266 let buf = self.read_buf.push_buf();
267 let count = match self.inner.read_packet(buf) {
268 Ok(c) => c,
269 Err(UsbError::WouldBlock) => 0,
270 Err(_) => {
271 self.read_error = true;
272 return;
273 }
274 };
275
276 if count > 0 {
277 self.read_buf.push(count);
278 self.read_waker.wake();
279 }
280 }
281 }
282
283 fn control_in(&mut self, xfer: ControlIn<B>) {
284 self.inner.control_in(xfer);
285 }
286
287 fn control_out(&mut self, xfer: ControlOut<B>) {
288 self.inner.control_out(xfer);
289 }
290}
diff --git a/embassy-stm32f4/src/util/mod.rs b/embassy-stm32f4/src/util/mod.rs
new file mode 100644
index 000000000..cf3306545
--- /dev/null
+++ b/embassy-stm32f4/src/util/mod.rs
@@ -0,0 +1,12 @@
1pub mod peripheral;
2pub mod ring_buffer;
3
4/// Low power blocking wait loop using WFE/SEV.
5pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) {
6 while !condition() {
7 // WFE might "eat" an event that would have otherwise woken the executor.
8 cortex_m::asm::wfe();
9 }
10 // Retrigger an event to be transparent to the executor.
11 cortex_m::asm::sev();
12}
diff --git a/embassy-stm32f4/src/util/peripheral.rs b/embassy-stm32f4/src/util/peripheral.rs
new file mode 100644
index 000000000..f2c7912ff
--- /dev/null
+++ b/embassy-stm32f4/src/util/peripheral.rs
@@ -0,0 +1,78 @@
1use core::cell::UnsafeCell;
2use core::marker::{PhantomData, PhantomPinned};
3use core::pin::Pin;
4use core::sync::atomic::{compiler_fence, Ordering};
5
6use crate::interrupt::OwnedInterrupt;
7
8pub trait PeripheralState {
9 type Interrupt: OwnedInterrupt;
10 fn on_interrupt(&mut self);
11}
12
13pub struct PeripheralMutex<S: PeripheralState> {
14 inner: Option<(UnsafeCell<S>, S::Interrupt)>,
15 _not_send: PhantomData<*mut ()>,
16 _pinned: PhantomPinned,
17}
18
19impl<S: PeripheralState> PeripheralMutex<S> {
20 pub fn new(state: S, irq: S::Interrupt) -> Self {
21 Self {
22 inner: Some((UnsafeCell::new(state), irq)),
23 _not_send: PhantomData,
24 _pinned: PhantomPinned,
25 }
26 }
27
28 pub fn with<R>(self: Pin<&mut Self>, f: impl FnOnce(&mut S, &mut S::Interrupt) -> R) -> R {
29 let this = unsafe { self.get_unchecked_mut() };
30 let (state, irq) = unwrap!(this.inner.as_mut());
31
32 irq.disable();
33 compiler_fence(Ordering::SeqCst);
34
35 irq.set_handler(
36 |p| {
37 // Safety: it's OK to get a &mut to the state, since
38 // - We're in the IRQ, no one else can't preempt us
39 // - We can't have preempted a with() call because the irq is disabled during it.
40 let state = unsafe { &mut *(p as *mut S) };
41 state.on_interrupt();
42 },
43 state.get() as *mut (),
44 );
45
46 // Safety: it's OK to get a &mut to the state, since the irq is disabled.
47 let state = unsafe { &mut *state.get() };
48
49 let r = f(state, irq);
50
51 compiler_fence(Ordering::SeqCst);
52 irq.enable();
53
54 r
55 }
56
57 pub fn try_free(self: Pin<&mut Self>) -> Option<(S, S::Interrupt)> {
58 let this = unsafe { self.get_unchecked_mut() };
59 this.inner.take().map(|(state, irq)| {
60 irq.disable();
61 irq.remove_handler();
62 (state.into_inner(), irq)
63 })
64 }
65
66 pub fn free(self: Pin<&mut Self>) -> (S, S::Interrupt) {
67 unwrap!(self.try_free())
68 }
69}
70
71impl<S: PeripheralState> Drop for PeripheralMutex<S> {
72 fn drop(&mut self) {
73 if let Some((_state, irq)) = &mut self.inner {
74 irq.disable();
75 irq.remove_handler();
76 }
77 }
78}
diff --git a/embassy-stm32f4/src/util/ring_buffer.rs b/embassy-stm32f4/src/util/ring_buffer.rs
new file mode 100644
index 000000000..0ef66f00a
--- /dev/null
+++ b/embassy-stm32f4/src/util/ring_buffer.rs
@@ -0,0 +1,86 @@
1use crate::fmt::{assert, *};
2
3pub struct RingBuffer<'a> {
4 buf: &'a mut [u8],
5 start: usize,
6 end: usize,
7 empty: bool,
8}
9
10impl<'a> RingBuffer<'a> {
11 pub fn new(buf: &'a mut [u8]) -> Self {
12 Self {
13 buf,
14 start: 0,
15 end: 0,
16 empty: true,
17 }
18 }
19
20 pub fn push_buf(&mut self) -> &mut [u8] {
21 if self.start == self.end && !self.empty {
22 trace!(" ringbuf: push_buf empty");
23 return &mut self.buf[..0];
24 }
25
26 let n = if self.start <= self.end {
27 self.buf.len() - self.end
28 } else {
29 self.start - self.end
30 };
31
32 trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n);
33 &mut self.buf[self.end..self.end + n]
34 }
35
36 pub fn push(&mut self, n: usize) {
37 trace!(" ringbuf: push {:?}", n);
38 if n == 0 {
39 return;
40 }
41
42 self.end = self.wrap(self.end + n);
43 self.empty = false;
44 }
45
46 pub fn pop_buf(&mut self) -> &mut [u8] {
47 if self.empty {
48 trace!(" ringbuf: pop_buf empty");
49 return &mut self.buf[..0];
50 }
51
52 let n = if self.end <= self.start {
53 self.buf.len() - self.start
54 } else {
55 self.end - self.start
56 };
57
58 trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n);
59 &mut self.buf[self.start..self.start + n]
60 }
61
62 pub fn pop(&mut self, n: usize) {
63 trace!(" ringbuf: pop {:?}", n);
64 if n == 0 {
65 return;
66 }
67
68 self.start = self.wrap(self.start + n);
69 self.empty = self.start == self.end;
70 }
71
72 pub fn clear(&mut self) {
73 self.start = 0;
74 self.end = 0;
75 self.empty = true;
76 }
77
78 fn wrap(&self, n: usize) -> usize {
79 assert!(n <= self.buf.len());
80 if n == self.buf.len() {
81 0
82 } else {
83 n
84 }
85 }
86}