aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/modules/ROOT/pages/bootloader.adoc2
-rw-r--r--embassy-boot/stm32/Cargo.toml2
-rw-r--r--embassy-boot/stm32/src/lib.rs2
-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-net/Cargo.toml3
-rw-r--r--embassy-net/src/device.rs9
-rw-r--r--embassy-net/src/lib.rs9
-rw-r--r--embassy-net/src/tcp/io_impl.rs67
-rw-r--r--embassy-net/src/tcp/mod.rs (renamed from embassy-net/src/tcp_socket.rs)131
-rw-r--r--embassy-nrf/Cargo.toml5
-rw-r--r--embassy-nrf/src/buffered_uarte.rs144
-rw-r--r--embassy-nrf/src/lib.rs1
-rw-r--r--embassy-stm32/Cargo.toml10
-rw-r--r--embassy-stm32/build.rs377
-rw-r--r--embassy-stm32/src/eth/mod.rs2
-rw-r--r--embassy-stm32/src/flash/f3.rs104
-rw-r--r--embassy-stm32/src/flash/f7.rs138
-rw-r--r--embassy-stm32/src/flash/h7.rs202
-rw-r--r--embassy-stm32/src/flash/l.rs185
-rw-r--r--embassy-stm32/src/flash/mod.rs193
-rw-r--r--embassy-stm32/src/lib.rs8
-rw-r--r--embassy-stm32/src/usart/buffered.rs238
-rw-r--r--embassy-stm32/src/usart/mod.rs225
-rw-r--r--embassy-stm32/src/usb_otg.rs61
-rw-r--r--embassy/src/io/error.rs142
-rw-r--r--embassy/src/io/mod.rs11
-rw-r--r--embassy/src/io/std.rs41
-rw-r--r--embassy/src/io/traits.rs175
-rw-r--r--embassy/src/io/util/copy_buf.rs80
-rw-r--r--embassy/src/io/util/drain.rs41
-rw-r--r--embassy/src/io/util/flush.rs32
-rw-r--r--embassy/src/io/util/mod.rs177
-rw-r--r--embassy/src/io/util/read.rs39
-rw-r--r--embassy/src/io/util/read_buf.rs34
-rw-r--r--embassy/src/io/util/read_byte.rs36
-rw-r--r--embassy/src/io/util/read_exact.rs48
-rw-r--r--embassy/src/io/util/read_to_end.rs48
-rw-r--r--embassy/src/io/util/read_while.rs61
-rw-r--r--embassy/src/io/util/skip_while.rs45
-rw-r--r--embassy/src/io/util/split.rs43
-rw-r--r--embassy/src/io/util/write.rs33
-rw-r--r--embassy/src/io/util/write_all.rs44
-rw-r--r--embassy/src/io/util/write_byte.rs39
-rw-r--r--embassy/src/lib.rs1
-rw-r--r--examples/boot/stm32f3/.cargo/config.toml6
-rw-r--r--examples/boot/stm32f3/Cargo.toml26
-rw-r--r--examples/boot/stm32f3/README.md29
-rw-r--r--examples/boot/stm32f3/build.rs37
-rw-r--r--examples/boot/stm32f3/memory.x15
-rw-r--r--examples/boot/stm32f3/src/bin/a.rs44
-rw-r--r--examples/boot/stm32f3/src/bin/b.rs25
-rw-r--r--examples/boot/stm32f7/.cargo/config.toml6
-rw-r--r--examples/boot/stm32f7/Cargo.toml26
-rw-r--r--examples/boot/stm32f7/README.md29
-rw-r--r--examples/boot/stm32f7/build.rs37
-rwxr-xr-xexamples/boot/stm32f7/flash-boot.sh8
-rw-r--r--examples/boot/stm32f7/memory-bl.x18
-rw-r--r--examples/boot/stm32f7/memory.x15
-rw-r--r--examples/boot/stm32f7/src/bin/a.rs44
-rw-r--r--examples/boot/stm32f7/src/bin/b.rs27
-rw-r--r--examples/boot/stm32h7/.cargo/config.toml6
-rw-r--r--examples/boot/stm32h7/Cargo.toml26
-rw-r--r--examples/boot/stm32h7/README.md29
-rw-r--r--examples/boot/stm32h7/build.rs37
-rwxr-xr-xexamples/boot/stm32h7/flash-boot.sh8
-rw-r--r--examples/boot/stm32h7/memory-bl.x18
-rw-r--r--examples/boot/stm32h7/memory.x15
-rw-r--r--examples/boot/stm32h7/src/bin/a.rs44
-rw-r--r--examples/boot/stm32h7/src/bin/b.rs27
-rw-r--r--examples/nrf/Cargo.toml3
-rw-r--r--examples/nrf/src/bin/buffered_uart.rs2
-rw-r--r--examples/nrf/src/bin/usb_ethernet.rs8
-rw-r--r--examples/std/Cargo.toml3
-rw-r--r--examples/std/src/bin/net.rs5
-rw-r--r--examples/std/src/bin/serial.rs14
-rw-r--r--examples/stm32f3/Cargo.toml1
-rw-r--r--examples/stm32f3/src/bin/blinky.rs2
-rw-r--r--examples/stm32f3/src/bin/flash.rs43
-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/stm32f7/Cargo.toml2
-rw-r--r--examples/stm32f7/src/bin/eth.rs7
-rw-r--r--examples/stm32f7/src/bin/flash.rs59
-rw-r--r--examples/stm32h7/Cargo.toml4
-rw-r--r--examples/stm32h7/src/bin/eth.rs7
-rw-r--r--examples/stm32h7/src/bin/flash.rs58
-rw-r--r--examples/stm32l0/.cargo/config.toml2
-rw-r--r--examples/stm32l0/Cargo.toml4
-rw-r--r--examples/stm32l0/src/bin/usart_irq.rs11
-rw-r--r--examples/stm32l4/Cargo.toml2
-rw-r--r--examples/stm32l4/src/bin/usb_uart.rs115
m---------stm32-data0
97 files changed, 2136 insertions, 3273 deletions
diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc
index 7539774c4..3df2daf51 100644
--- a/docs/modules/ROOT/pages/bootloader.adoc
+++ b/docs/modules/ROOT/pages/bootloader.adoc
@@ -14,7 +14,7 @@ The bootloader supports both internal and external flash by relying on the `embe
14The bootloader supports 14The bootloader supports
15 15
16* nRF52 with and without softdevice 16* nRF52 with and without softdevice
17* STM32 L4, WB, WL, L1 and L0 17* STM32 L4, WB, WL, L1, L0, F3, F7 and H7
18 18
19In general, the bootloader works on any platform that implements the `embedded-storage` traits for its internal flash, but may require custom initialization code to work. 19In general, the bootloader works on any platform that implements the `embedded-storage` traits for its internal flash, but may require custom initialization code to work.
20 20
diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml
index a706e4c06..78339b0a2 100644
--- a/embassy-boot/stm32/Cargo.toml
+++ b/embassy-boot/stm32/Cargo.toml
@@ -2,7 +2,7 @@
2authors = [ 2authors = [
3 "Ulf Lilleengen <[email protected]>", 3 "Ulf Lilleengen <[email protected]>",
4] 4]
5edition = "2018" 5edition = "2021"
6name = "embassy-boot-stm32" 6name = "embassy-boot-stm32"
7version = "0.1.0" 7version = "0.1.0"
8description = "Bootloader for STM32 chips" 8description = "Bootloader for STM32 chips"
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs
index 82e32a97d..48512534b 100644
--- a/embassy-boot/stm32/src/lib.rs
+++ b/embassy-boot/stm32/src/lib.rs
@@ -67,7 +67,7 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
67 [(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:, 67 [(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
68 { 68 {
69 match self.boot.prepare_boot(flash) { 69 match self.boot.prepare_boot(flash) {
70 Ok(_) => self.boot.boot_address(), 70 Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(),
71 Err(_) => panic!("boot prepare error!"), 71 Err(_) => panic!("boot prepare error!"),
72 } 72 }
73 } 73 }
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-net/Cargo.toml b/embassy-net/Cargo.toml
index 2d0116bd5..1b2847a0d 100644
--- a/embassy-net/Cargo.toml
+++ b/embassy-net/Cargo.toml
@@ -31,12 +31,15 @@ pool-32 = []
31pool-64 = [] 31pool-64 = []
32pool-128 = [] 32pool-128 = []
33 33
34nightly = ["embedded-io/async"]
35
34[dependencies] 36[dependencies]
35 37
36defmt = { version = "0.3", optional = true } 38defmt = { version = "0.3", optional = true }
37log = { version = "0.4.14", optional = true } 39log = { version = "0.4.14", optional = true }
38 40
39embassy = { version = "0.1.0", path = "../embassy" } 41embassy = { version = "0.1.0", path = "../embassy" }
42embedded-io = "0.2.0"
40 43
41managed = { version = "0.8.0", default-features = false, features = [ "map" ] } 44managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
42heapless = { version = "0.7.5", default-features = false } 45heapless = { version = "0.7.5", default-features = false }
diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs
index f66ebc193..1f4fa5208 100644
--- a/embassy-net/src/device.rs
+++ b/embassy-net/src/device.rs
@@ -4,7 +4,6 @@ use smoltcp::phy::DeviceCapabilities;
4use smoltcp::time::Instant as SmolInstant; 4use smoltcp::time::Instant as SmolInstant;
5 5
6use crate::packet_pool::PacketBoxExt; 6use crate::packet_pool::PacketBoxExt;
7use crate::Result;
8use crate::{Packet, PacketBox, PacketBuf}; 7use crate::{Packet, PacketBox, PacketBuf};
9 8
10#[derive(PartialEq, Eq, Clone, Copy)] 9#[derive(PartialEq, Eq, Clone, Copy)]
@@ -78,9 +77,9 @@ pub struct RxToken {
78} 77}
79 78
80impl smoltcp::phy::RxToken for RxToken { 79impl smoltcp::phy::RxToken for RxToken {
81 fn consume<R, F>(mut self, _timestamp: SmolInstant, f: F) -> Result<R> 80 fn consume<R, F>(mut self, _timestamp: SmolInstant, f: F) -> smoltcp::Result<R>
82 where 81 where
83 F: FnOnce(&mut [u8]) -> Result<R>, 82 F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
84 { 83 {
85 f(&mut self.pkt) 84 f(&mut self.pkt)
86 } 85 }
@@ -92,9 +91,9 @@ pub struct TxToken<'a> {
92} 91}
93 92
94impl<'a> smoltcp::phy::TxToken for TxToken<'a> { 93impl<'a> smoltcp::phy::TxToken for TxToken<'a> {
95 fn consume<R, F>(self, _timestamp: SmolInstant, len: usize, f: F) -> Result<R> 94 fn consume<R, F>(self, _timestamp: SmolInstant, len: usize, f: F) -> smoltcp::Result<R>
96 where 95 where
97 F: FnOnce(&mut [u8]) -> Result<R>, 96 F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
98 { 97 {
99 let mut buf = self.pkt.slice(0..len); 98 let mut buf = self.pkt.slice(0..len);
100 let r = f(&mut buf)?; 99 let r = f(&mut buf)?;
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index ffe786b36..ded841909 100644
--- a/embassy-net/src/lib.rs
+++ b/embassy-net/src/lib.rs
@@ -1,5 +1,9 @@
1#![cfg_attr(not(feature = "std"), no_std)] 1#![cfg_attr(not(feature = "std"), no_std)]
2#![allow(clippy::new_without_default)] 2#![allow(clippy::new_without_default)]
3#![cfg_attr(
4 feature = "nightly",
5 feature(generic_associated_types, type_alias_impl_trait)
6)]
3 7
4// This mod MUST go first, so that the others see its macros. 8// This mod MUST go first, so that the others see its macros.
5pub(crate) mod fmt; 9pub(crate) mod fmt;
@@ -20,9 +24,7 @@ pub use stack::{
20}; 24};
21 25
22#[cfg(feature = "tcp")] 26#[cfg(feature = "tcp")]
23mod tcp_socket; 27pub mod tcp;
24#[cfg(feature = "tcp")]
25pub use tcp_socket::TcpSocket;
26 28
27// smoltcp reexports 29// smoltcp reexports
28pub use smoltcp::phy::{DeviceCapabilities, Medium}; 30pub use smoltcp::phy::{DeviceCapabilities, Medium};
@@ -32,4 +34,3 @@ pub use smoltcp::time::Instant as SmolInstant;
32pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; 34pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
33pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; 35pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr};
34pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>; 36pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>;
35pub use smoltcp::{Error, Result};
diff --git a/embassy-net/src/tcp/io_impl.rs b/embassy-net/src/tcp/io_impl.rs
new file mode 100644
index 000000000..155733497
--- /dev/null
+++ b/embassy-net/src/tcp/io_impl.rs
@@ -0,0 +1,67 @@
1use core::future::Future;
2use core::task::Poll;
3use futures::future::poll_fn;
4
5use super::{Error, TcpSocket};
6
7impl<'d> embedded_io::asynch::Read for TcpSocket<'d> {
8 type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
9 where
10 Self: 'a;
11
12 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
13 poll_fn(move |cx| {
14 // CAUTION: smoltcp semantics around EOF are different to what you'd expect
15 // from posix-like IO, so we have to tweak things here.
16 self.with(|s, _| match s.recv_slice(buf) {
17 // No data ready
18 Ok(0) => {
19 s.register_recv_waker(cx.waker());
20 Poll::Pending
21 }
22 // Data ready!
23 Ok(n) => Poll::Ready(Ok(n)),
24 // EOF
25 Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)),
26 // Connection reset. TODO: this can also be timeouts etc, investigate.
27 Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)),
28 // smoltcp returns no errors other than the above.
29 Err(_) => unreachable!(),
30 })
31 })
32 }
33}
34
35impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
36 type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
37 where
38 Self: 'a;
39
40 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
41 poll_fn(move |cx| {
42 self.with(|s, _| match s.send_slice(buf) {
43 // Not ready to send (no space in the tx buffer)
44 Ok(0) => {
45 s.register_send_waker(cx.waker());
46 Poll::Pending
47 }
48 // Some data sent
49 Ok(n) => Poll::Ready(Ok(n)),
50 // Connection reset. TODO: this can also be timeouts etc, investigate.
51 Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)),
52 // smoltcp returns no errors other than the above.
53 Err(_) => unreachable!(),
54 })
55 })
56 }
57
58 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
59 where
60 Self: 'a;
61
62 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
63 poll_fn(move |_| {
64 Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
65 })
66 }
67}
diff --git a/embassy-net/src/tcp_socket.rs b/embassy-net/src/tcp/mod.rs
index 5637505d4..3bfd4c7b6 100644
--- a/embassy-net/src/tcp_socket.rs
+++ b/embassy-net/src/tcp/mod.rs
@@ -1,17 +1,46 @@
1use core::marker::PhantomData; 1use core::marker::PhantomData;
2use core::mem; 2use core::mem;
3use core::pin::Pin; 3use core::task::Poll;
4use core::task::{Context, Poll};
5use embassy::io;
6use embassy::io::{AsyncBufRead, AsyncWrite};
7use smoltcp::iface::{Context as SmolContext, SocketHandle}; 4use smoltcp::iface::{Context as SmolContext, SocketHandle};
8use smoltcp::socket::TcpSocket as SyncTcpSocket; 5use smoltcp::socket::TcpSocket as SyncTcpSocket;
9use smoltcp::socket::{TcpSocketBuffer, TcpState}; 6use smoltcp::socket::{TcpSocketBuffer, TcpState};
10use smoltcp::time::Duration; 7use smoltcp::time::Duration;
11use smoltcp::wire::IpEndpoint; 8use smoltcp::wire::IpEndpoint;
12 9
10#[cfg(feature = "nightly")]
11mod io_impl;
12
13use super::stack::Stack; 13use super::stack::Stack;
14use crate::{Error, Result}; 14
15#[derive(PartialEq, Eq, Clone, Copy, Debug)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub enum Error {
18 ConnectionReset,
19}
20
21#[derive(PartialEq, Eq, Clone, Copy, Debug)]
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23pub enum ConnectError {
24 /// The socket is already connected or listening.
25 InvalidState,
26 /// The remote host rejected the connection with a RST packet.
27 ConnectionReset,
28 /// Connect timed out.
29 TimedOut,
30 /// No route to host.
31 NoRoute,
32}
33
34#[derive(PartialEq, Eq, Clone, Copy, Debug)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36pub enum AcceptError {
37 /// The socket is already connected or listening.
38 InvalidState,
39 /// Invalid listen port
40 InvalidPort,
41 /// The remote host rejected the connection with a RST packet.
42 ConnectionReset,
43}
15 44
16pub struct TcpSocket<'a> { 45pub struct TcpSocket<'a> {
17 handle: SocketHandle, 46 handle: SocketHandle,
@@ -37,17 +66,25 @@ impl<'a> TcpSocket<'a> {
37 } 66 }
38 } 67 }
39 68
40 pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<()> 69 pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError>
41 where 70 where
42 T: Into<IpEndpoint>, 71 T: Into<IpEndpoint>,
43 { 72 {
44 let local_port = Stack::with(|stack| stack.get_local_port()); 73 let local_port = Stack::with(|stack| stack.get_local_port());
45 self.with(|s, cx| s.connect(cx, remote_endpoint, local_port))?; 74 match self.with(|s, cx| s.connect(cx, remote_endpoint, local_port)) {
75 Ok(()) => {}
76 Err(smoltcp::Error::Illegal) => return Err(ConnectError::InvalidState),
77 Err(smoltcp::Error::Unaddressable) => return Err(ConnectError::NoRoute),
78 // smoltcp returns no errors other than the above.
79 Err(_) => unreachable!(),
80 }
46 81
47 futures::future::poll_fn(|cx| { 82 futures::future::poll_fn(|cx| {
48 self.with(|s, _| match s.state() { 83 self.with(|s, _| match s.state() {
49 TcpState::Closed | TcpState::TimeWait => Poll::Ready(Err(Error::Unaddressable)), 84 TcpState::Closed | TcpState::TimeWait => {
50 TcpState::Listen => Poll::Ready(Err(Error::Illegal)), 85 Poll::Ready(Err(ConnectError::ConnectionReset))
86 }
87 TcpState::Listen => unreachable!(),
51 TcpState::SynSent | TcpState::SynReceived => { 88 TcpState::SynSent | TcpState::SynReceived => {
52 s.register_send_waker(cx.waker()); 89 s.register_send_waker(cx.waker());
53 Poll::Pending 90 Poll::Pending
@@ -58,11 +95,17 @@ impl<'a> TcpSocket<'a> {
58 .await 95 .await
59 } 96 }
60 97
61 pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<()> 98 pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError>
62 where 99 where
63 T: Into<IpEndpoint>, 100 T: Into<IpEndpoint>,
64 { 101 {
65 self.with(|s, _| s.listen(local_endpoint))?; 102 match self.with(|s, _| s.listen(local_endpoint)) {
103 Ok(()) => {}
104 Err(smoltcp::Error::Illegal) => return Err(AcceptError::InvalidState),
105 Err(smoltcp::Error::Unaddressable) => return Err(AcceptError::InvalidPort),
106 // smoltcp returns no errors other than the above.
107 Err(_) => unreachable!(),
108 }
66 109
67 futures::future::poll_fn(|cx| { 110 futures::future::poll_fn(|cx| {
68 self.with(|s, _| match s.state() { 111 self.with(|s, _| match s.state() {
@@ -130,11 +173,6 @@ impl<'a> TcpSocket<'a> {
130 } 173 }
131} 174}
132 175
133fn to_ioerr(_err: Error) -> io::Error {
134 // todo
135 io::Error::Other
136}
137
138impl<'a> Drop for TcpSocket<'a> { 176impl<'a> Drop for TcpSocket<'a> {
139 fn drop(&mut self) { 177 fn drop(&mut self) {
140 Stack::with(|stack| { 178 Stack::with(|stack| {
@@ -143,63 +181,12 @@ impl<'a> Drop for TcpSocket<'a> {
143 } 181 }
144} 182}
145 183
146impl<'a> AsyncBufRead for TcpSocket<'a> { 184impl embedded_io::Error for Error {
147 fn poll_fill_buf<'z>( 185 fn kind(&self) -> embedded_io::ErrorKind {
148 self: Pin<&'z mut Self>, 186 embedded_io::ErrorKind::Other
149 cx: &mut Context<'_>,
150 ) -> Poll<io::Result<&'z [u8]>> {
151 self.with(|s, _| match s.peek(1 << 30) {
152 // No data ready
153 Ok(buf) if buf.is_empty() => {
154 s.register_recv_waker(cx.waker());
155 Poll::Pending
156 }
157 // Data ready!
158 Ok(buf) => {
159 // Safety:
160 // - User can't touch the inner TcpSocket directly at all.
161 // - The socket itself won't touch these bytes until consume() is called, which
162 // requires the user to release this borrow.
163 let buf: &'z [u8] = unsafe { core::mem::transmute(&*buf) };
164 Poll::Ready(Ok(buf))
165 }
166 // EOF
167 Err(Error::Finished) => Poll::Ready(Ok(&[][..])),
168 // Error
169 Err(e) => Poll::Ready(Err(to_ioerr(e))),
170 })
171 }
172
173 fn consume(self: Pin<&mut Self>, amt: usize) {
174 if amt == 0 {
175 // smoltcp's recv returns Finished if we're at EOF,
176 // even if we're "reading" 0 bytes.
177 return;
178 }
179 self.with(|s, _| s.recv(|_| (amt, ()))).unwrap()
180 } 187 }
181} 188}
182 189
183impl<'a> AsyncWrite for TcpSocket<'a> { 190impl<'d> embedded_io::Io for TcpSocket<'d> {
184 fn poll_write( 191 type Error = Error;
185 self: Pin<&mut Self>,
186 cx: &mut Context<'_>,
187 buf: &[u8],
188 ) -> Poll<io::Result<usize>> {
189 self.with(|s, _| match s.send_slice(buf) {
190 // Not ready to send (no space in the tx buffer)
191 Ok(0) => {
192 s.register_send_waker(cx.waker());
193 Poll::Pending
194 }
195 // Some data sent
196 Ok(n) => Poll::Ready(Ok(n)),
197 // Error
198 Err(e) => Poll::Ready(Err(to_ioerr(e))),
199 })
200 }
201
202 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
203 Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
204 }
205} 192}
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index b7c09286f..cf61abcc8 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -19,10 +19,10 @@ flavors = [
19 19
20time = ["embassy/time"] 20time = ["embassy/time"]
21 21
22defmt = ["dep:defmt", "embassy/defmt", "embassy-usb?/defmt"] 22defmt = ["dep:defmt", "embassy/defmt", "embassy-usb?/defmt", "embedded-io?/defmt"]
23 23
24# Enable nightly-only features 24# Enable nightly-only features
25nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async"] 25nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async", "dep:embedded-io"]
26 26
27# Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`. 27# Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`.
28# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. 28# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version.
@@ -73,6 +73,7 @@ embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional=true }
73embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 73embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
74embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} 74embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
75embedded-hal-async = { version = "0.1.0-alpha.0", optional = true} 75embedded-hal-async = { version = "0.1.0-alpha.0", optional = true}
76embedded-io = { version = "0.2.0", features = ["async"], optional = true }
76 77
77defmt = { version = "0.3", optional = true } 78defmt = { version = "0.3", optional = true }
78log = { version = "0.4.14", optional = true } 79log = { version = "0.4.14", optional = true }
diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs
index b49c12788..fc4e9c8d0 100644
--- a/embassy-nrf/src/buffered_uarte.rs
+++ b/embassy-nrf/src/buffered_uarte.rs
@@ -14,18 +14,17 @@
14//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used. 14//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.
15 15
16use core::cmp::min; 16use core::cmp::min;
17use core::future::Future;
17use core::marker::PhantomData; 18use core::marker::PhantomData;
18use core::mem;
19use core::pin::Pin;
20use core::sync::atomic::{compiler_fence, Ordering}; 19use core::sync::atomic::{compiler_fence, Ordering};
21use core::task::{Context, Poll}; 20use core::task::Poll;
22use embassy::interrupt::InterruptExt; 21use embassy::interrupt::InterruptExt;
23use embassy::io::{AsyncBufRead, AsyncWrite};
24use embassy::util::Unborrow; 22use embassy::util::Unborrow;
25use embassy::waitqueue::WakerRegistration; 23use embassy::waitqueue::WakerRegistration;
26use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; 24use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
27use embassy_hal_common::ring_buffer::RingBuffer; 25use embassy_hal_common::ring_buffer::RingBuffer;
28use embassy_hal_common::{low_power_wait_until, unborrow}; 26use embassy_hal_common::{low_power_wait_until, unborrow};
27use futures::future::poll_fn;
29 28
30use crate::gpio::Pin as GpioPin; 29use crate::gpio::Pin as GpioPin;
31use crate::pac; 30use crate::pac;
@@ -197,82 +196,99 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
197 } 196 }
198} 197}
199 198
200impl<'d, U: UarteInstance, T: TimerInstance> AsyncBufRead for BufferedUarte<'d, U, T> { 199impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
201 fn poll_fill_buf( 200 type Error = core::convert::Infallible;
202 mut self: Pin<&mut Self>, 201}
203 cx: &mut Context<'_>, 202
204 ) -> Poll<embassy::io::Result<&[u8]>> { 203impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
205 self.inner.with(|state| { 204 type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
206 compiler_fence(Ordering::SeqCst); 205 where
207 trace!("poll_read"); 206 Self: 'a;
208 207
209 // We have data ready in buffer? Return it. 208 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
210 let buf = state.rx.pop_buf(); 209 poll_fn(move |cx| {
211 if !buf.is_empty() { 210 let mut do_pend = false;
212 trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len()); 211 let res = self.inner.with(|state| {
213 let buf: &[u8] = buf; 212 compiler_fence(Ordering::SeqCst);
214 let buf: &[u8] = unsafe { mem::transmute(buf) }; 213 trace!("poll_read");
215 return Poll::Ready(Ok(buf)); 214
215 // We have data ready in buffer? Return it.
216 let data = state.rx.pop_buf();
217 if !data.is_empty() {
218 trace!(" got {:?} {:?}", data.as_ptr() as u32, data.len());
219 let len = data.len().min(data.len());
220 buf[..len].copy_from_slice(&data[..len]);
221 state.rx.pop(len);
222 do_pend = true;
223 return Poll::Ready(Ok(len));
224 }
225
226 trace!(" empty");
227 state.rx_waker.register(cx.waker());
228 Poll::Pending
229 });
230 if do_pend {
231 self.inner.pend();
216 } 232 }
217 233
218 trace!(" empty"); 234 res
219 state.rx_waker.register(cx.waker());
220 Poll::<embassy::io::Result<&[u8]>>::Pending
221 }) 235 })
222 } 236 }
223
224 fn consume(mut self: Pin<&mut Self>, amt: usize) {
225 self.inner.with(|state| {
226 trace!("consume {:?}", amt);
227 state.rx.pop(amt);
228 });
229 self.inner.pend();
230 }
231} 237}
232 238
233impl<'d, U: UarteInstance, T: TimerInstance> AsyncWrite for BufferedUarte<'d, U, T> { 239impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write
234 fn poll_write( 240 for BufferedUarte<'d, U, T>
235 mut self: Pin<&mut Self>, 241{
236 cx: &mut Context<'_>, 242 type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
237 buf: &[u8], 243 where
238 ) -> Poll<embassy::io::Result<usize>> { 244 Self: 'a;
239 let poll = self.inner.with(|state| { 245
240 trace!("poll_write: {:?}", buf.len()); 246 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
241 247 poll_fn(move |cx| {
242 let tx_buf = state.tx.push_buf(); 248 let res = self.inner.with(|state| {
243 if tx_buf.is_empty() { 249 trace!("poll_write: {:?}", buf.len());
244 trace!("poll_write: pending"); 250
245 state.tx_waker.register(cx.waker()); 251 let tx_buf = state.tx.push_buf();
246 return Poll::Pending; 252 if tx_buf.is_empty() {
247 } 253 trace!("poll_write: pending");
254 state.tx_waker.register(cx.waker());
255 return Poll::Pending;
256 }
248 257
249 let n = min(tx_buf.len(), buf.len()); 258 let n = min(tx_buf.len(), buf.len());
250 tx_buf[..n].copy_from_slice(&buf[..n]); 259 tx_buf[..n].copy_from_slice(&buf[..n]);
251 state.tx.push(n); 260 state.tx.push(n);
252 261
253 trace!("poll_write: queued {:?}", n); 262 trace!("poll_write: queued {:?}", n);
254 263
255 compiler_fence(Ordering::SeqCst); 264 compiler_fence(Ordering::SeqCst);
256 265
257 Poll::Ready(Ok(n)) 266 Poll::Ready(Ok(n))
258 }); 267 });
259 268
260 self.inner.pend(); 269 self.inner.pend();
261 270
262 poll 271 res
272 })
263 } 273 }
264 274
265 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<embassy::io::Result<()>> { 275 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
266 self.inner.with(|state| { 276 where
267 trace!("poll_flush"); 277 Self: 'a;
268 278
269 if !state.tx.is_empty() { 279 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
270 trace!("poll_flush: pending"); 280 poll_fn(move |cx| {
271 state.tx_waker.register(cx.waker()); 281 self.inner.with(|state| {
272 return Poll::Pending; 282 trace!("poll_flush");
273 } 283
284 if !state.tx.is_empty() {
285 trace!("poll_flush: pending");
286 state.tx_waker.register(cx.waker());
287 return Poll::Pending;
288 }
274 289
275 Poll::Ready(Ok(())) 290 Poll::Ready(Ok(()))
291 })
276 }) 292 })
277 } 293 }
278} 294}
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 46234b4b5..9c298a8b0 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -64,6 +64,7 @@ pub(crate) mod util;
64#[cfg(feature = "_time-driver")] 64#[cfg(feature = "_time-driver")]
65mod time_driver; 65mod time_driver;
66 66
67#[cfg(feature = "nightly")]
67pub mod buffered_uarte; 68pub mod buffered_uarte;
68pub mod gpio; 69pub mod gpio;
69#[cfg(feature = "gpiote")] 70#[cfg(feature = "gpiote")]
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 8152b07d4..9686e016e 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -53,17 +53,17 @@ 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"
60stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } 59stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] }
61vcell = { version = "0.1.3", optional = true } 60vcell = "0.1.3"
62bxcan = "0.6.2" 61bxcan = "0.6.2"
63nb = "1.0.0" 62nb = "1.0.0"
64stm32-fmc = "0.2.4" 63stm32-fmc = "0.2.4"
65seq-macro = "0.2.2" 64seq-macro = "0.2.2"
66cfg-if = "1.0.0" 65cfg-if = "1.0.0"
66embedded-io = { version = "0.2.0", features = ["async"], optional = true }
67 67
68[build-dependencies] 68[build-dependencies]
69proc-macro2 = "1.0.36" 69proc-macro2 = "1.0.36"
@@ -71,12 +71,12 @@ quote = "1.0.15"
71stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} 71stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
72 72
73[features] 73[features]
74defmt = ["dep:defmt", "embassy/defmt", "embedded-io?/defmt" ]
74sdmmc-rs = ["embedded-sdmmc"] 75sdmmc-rs = ["embedded-sdmmc"]
75net = ["embassy-net", "vcell"] 76net = ["embassy-net" ]
76memory-x = ["stm32-metapac/memory-x"] 77memory-x = ["stm32-metapac/memory-x"]
77subghz = [] 78subghz = []
78exti = [] 79exti = []
79usb-otg = ["synopsys-usb-otg"]
80 80
81# Features starting with `_` are for internal use only. They're not intended 81# 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. 82# to be enabled by other crates, and are not covered by semver guarantees.
@@ -90,7 +90,7 @@ time-driver-tim12 = ["_time-driver"]
90time-driver-tim15 = ["_time-driver"] 90time-driver-tim15 = ["_time-driver"]
91 91
92# Enable nightly-only features 92# Enable nightly-only features
93nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async"] 93nightly = ["embassy/nightly", "embassy-net?/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io"]
94 94
95# Reexport stm32-metapac at `embassy_stm32::pac`. 95# Reexport stm32-metapac at `embassy_stm32::pac`.
96# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. 96# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version.
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index aa3e64131..490f2d8f2 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -244,200 +244,200 @@ fn main() {
244 244
245 #[rustfmt::skip] 245 #[rustfmt::skip]
246 let signals: HashMap<_, _> = [ 246 let signals: HashMap<_, _> = [
247 // (kind, signal) => (trait, cfgs) 247 // (kind, signal) => trait
248 (("usart", "TX"), (quote!(crate::usart::TxPin), quote!())), 248 (("usart", "TX"), quote!(crate::usart::TxPin)),
249 (("usart", "RX"), (quote!(crate::usart::RxPin), quote!())), 249 (("usart", "RX"), quote!(crate::usart::RxPin)),
250 (("usart", "CTS"), (quote!(crate::usart::CtsPin), quote!())), 250 (("usart", "CTS"), quote!(crate::usart::CtsPin)),
251 (("usart", "RTS"), (quote!(crate::usart::RtsPin), quote!())), 251 (("usart", "RTS"), quote!(crate::usart::RtsPin)),
252 (("usart", "CK"), (quote!(crate::usart::CkPin), quote!())), 252 (("usart", "CK"), quote!(crate::usart::CkPin)),
253 (("usart", "TX"), (quote!(crate::usart::TxPin), quote!())), 253 (("usart", "TX"), quote!(crate::usart::TxPin)),
254 (("usart", "RX"), (quote!(crate::usart::RxPin), quote!())), 254 (("usart", "RX"), quote!(crate::usart::RxPin)),
255 (("usart", "CTS"), (quote!(crate::usart::CtsPin), quote!())), 255 (("usart", "CTS"), quote!(crate::usart::CtsPin)),
256 (("usart", "RTS"), (quote!(crate::usart::RtsPin), quote!())), 256 (("usart", "RTS"), quote!(crate::usart::RtsPin)),
257 (("usart", "CK"), (quote!(crate::usart::CkPin), quote!())), 257 (("usart", "CK"), quote!(crate::usart::CkPin)),
258 (("spi", "SCK"), (quote!(crate::spi::SckPin), quote!())), 258 (("spi", "SCK"), quote!(crate::spi::SckPin)),
259 (("spi", "MOSI"), (quote!(crate::spi::MosiPin), quote!())), 259 (("spi", "MOSI"), quote!(crate::spi::MosiPin)),
260 (("spi", "MISO"), (quote!(crate::spi::MisoPin), quote!())), 260 (("spi", "MISO"), quote!(crate::spi::MisoPin)),
261 (("i2c", "SDA"), (quote!(crate::i2c::SdaPin), quote!())), 261 (("i2c", "SDA"), quote!(crate::i2c::SdaPin)),
262 (("i2c", "SCL"), (quote!(crate::i2c::SclPin), quote!())), 262 (("i2c", "SCL"), quote!(crate::i2c::SclPin)),
263 (("rcc", "MCO_1"), (quote!(crate::rcc::McoPin), quote!())), 263 (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)),
264 (("rcc", "MCO_2"), (quote!(crate::rcc::McoPin), quote!())), 264 (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)),
265 (("dcmi", "D0"), (quote!(crate::dcmi::D0Pin), quote!())), 265 (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)),
266 (("dcmi", "D1"), (quote!(crate::dcmi::D1Pin), quote!())), 266 (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)),
267 (("dcmi", "D2"), (quote!(crate::dcmi::D2Pin), quote!())), 267 (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)),
268 (("dcmi", "D3"), (quote!(crate::dcmi::D3Pin), quote!())), 268 (("dcmi", "D3"), quote!(crate::dcmi::D3Pin)),
269 (("dcmi", "D4"), (quote!(crate::dcmi::D4Pin), quote!())), 269 (("dcmi", "D4"), quote!(crate::dcmi::D4Pin)),
270 (("dcmi", "D5"), (quote!(crate::dcmi::D5Pin), quote!())), 270 (("dcmi", "D5"), quote!(crate::dcmi::D5Pin)),
271 (("dcmi", "D6"), (quote!(crate::dcmi::D6Pin), quote!())), 271 (("dcmi", "D6"), quote!(crate::dcmi::D6Pin)),
272 (("dcmi", "D7"), (quote!(crate::dcmi::D7Pin), quote!())), 272 (("dcmi", "D7"), quote!(crate::dcmi::D7Pin)),
273 (("dcmi", "D8"), (quote!(crate::dcmi::D8Pin), quote!())), 273 (("dcmi", "D8"), quote!(crate::dcmi::D8Pin)),
274 (("dcmi", "D9"), (quote!(crate::dcmi::D9Pin), quote!())), 274 (("dcmi", "D9"), quote!(crate::dcmi::D9Pin)),
275 (("dcmi", "D10"), (quote!(crate::dcmi::D10Pin), quote!())), 275 (("dcmi", "D10"), quote!(crate::dcmi::D10Pin)),
276 (("dcmi", "D11"), (quote!(crate::dcmi::D11Pin), quote!())), 276 (("dcmi", "D11"), quote!(crate::dcmi::D11Pin)),
277 (("dcmi", "D12"), (quote!(crate::dcmi::D12Pin), quote!())), 277 (("dcmi", "D12"), quote!(crate::dcmi::D12Pin)),
278 (("dcmi", "D13"), (quote!(crate::dcmi::D13Pin), quote!())), 278 (("dcmi", "D13"), quote!(crate::dcmi::D13Pin)),
279 (("dcmi", "HSYNC"), (quote!(crate::dcmi::HSyncPin), quote!())), 279 (("dcmi", "HSYNC"), quote!(crate::dcmi::HSyncPin)),
280 (("dcmi", "VSYNC"), (quote!(crate::dcmi::VSyncPin), quote!())), 280 (("dcmi", "VSYNC"), quote!(crate::dcmi::VSyncPin)),
281 (("dcmi", "PIXCLK"), (quote!(crate::dcmi::PixClkPin), quote!())), 281 (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)),
282 (("otgfs", "DP"), (quote!(crate::usb_otg::DpPin), quote!(#[cfg(feature="usb-otg")]))), 282 (("otgfs", "DP"), quote!(crate::usb_otg::DpPin)),
283 (("otgfs", "DM"), (quote!(crate::usb_otg::DmPin), quote!(#[cfg(feature="usb-otg")]))), 283 (("otgfs", "DM"), quote!(crate::usb_otg::DmPin)),
284 (("otghs", "DP"), (quote!(crate::usb_otg::DpPin), quote!(#[cfg(feature="usb-otg")]))), 284 (("otghs", "DP"), quote!(crate::usb_otg::DpPin)),
285 (("otghs", "DM"), (quote!(crate::usb_otg::DmPin), quote!(#[cfg(feature="usb-otg")]))), 285 (("otghs", "DM"), quote!(crate::usb_otg::DmPin)),
286 (("otghs", "ULPI_CK"), (quote!(crate::usb_otg::UlpiClkPin), quote!(#[cfg(feature="usb-otg")]))), 286 (("otghs", "ULPI_CK"), quote!(crate::usb_otg::UlpiClkPin)),
287 (("otghs", "ULPI_DIR"), (quote!(crate::usb_otg::UlpiDirPin), quote!(#[cfg(feature="usb-otg")]))), 287 (("otghs", "ULPI_DIR"), quote!(crate::usb_otg::UlpiDirPin)),
288 (("otghs", "ULPI_NXT"), (quote!(crate::usb_otg::UlpiNxtPin), quote!(#[cfg(feature="usb-otg")]))), 288 (("otghs", "ULPI_NXT"), quote!(crate::usb_otg::UlpiNxtPin)),
289 (("otghs", "ULPI_STP"), (quote!(crate::usb_otg::UlpiStpPin), quote!(#[cfg(feature="usb-otg")]))), 289 (("otghs", "ULPI_STP"), quote!(crate::usb_otg::UlpiStpPin)),
290 (("otghs", "ULPI_D0"), (quote!(crate::usb_otg::UlpiD0Pin), quote!(#[cfg(feature="usb-otg")]))), 290 (("otghs", "ULPI_D0"), quote!(crate::usb_otg::UlpiD0Pin)),
291 (("otghs", "ULPI_D1"), (quote!(crate::usb_otg::UlpiD1Pin), quote!(#[cfg(feature="usb-otg")]))), 291 (("otghs", "ULPI_D1"), quote!(crate::usb_otg::UlpiD1Pin)),
292 (("otghs", "ULPI_D2"), (quote!(crate::usb_otg::UlpiD2Pin), quote!(#[cfg(feature="usb-otg")]))), 292 (("otghs", "ULPI_D2"), quote!(crate::usb_otg::UlpiD2Pin)),
293 (("otghs", "ULPI_D3"), (quote!(crate::usb_otg::UlpiD3Pin), quote!(#[cfg(feature="usb-otg")]))), 293 (("otghs", "ULPI_D3"), quote!(crate::usb_otg::UlpiD3Pin)),
294 (("otghs", "ULPI_D4"), (quote!(crate::usb_otg::UlpiD4Pin), quote!(#[cfg(feature="usb-otg")]))), 294 (("otghs", "ULPI_D4"), quote!(crate::usb_otg::UlpiD4Pin)),
295 (("otghs", "ULPI_D5"), (quote!(crate::usb_otg::UlpiD5Pin), quote!(#[cfg(feature="usb-otg")]))), 295 (("otghs", "ULPI_D5"), quote!(crate::usb_otg::UlpiD5Pin)),
296 (("otghs", "ULPI_D6"), (quote!(crate::usb_otg::UlpiD6Pin), quote!(#[cfg(feature="usb-otg")]))), 296 (("otghs", "ULPI_D6"), quote!(crate::usb_otg::UlpiD6Pin)),
297 (("otghs", "ULPI_D7"), (quote!(crate::usb_otg::UlpiD7Pin), quote!(#[cfg(feature="usb-otg")]))), 297 (("otghs", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)),
298 (("can", "TX"), (quote!(crate::can::TxPin), quote!())), 298 (("can", "TX"), quote!(crate::can::TxPin)),
299 (("can", "RX"), (quote!(crate::can::RxPin), quote!())), 299 (("can", "RX"), quote!(crate::can::RxPin)),
300 (("eth", "REF_CLK"), (quote!(crate::eth::RefClkPin), quote!(#[cfg(feature="net")]))), 300 (("eth", "REF_CLK"), quote!(crate::eth::RefClkPin)),
301 (("eth", "MDIO"), (quote!(crate::eth::MDIOPin), quote!(#[cfg(feature="net")]))), 301 (("eth", "MDIO"), quote!(crate::eth::MDIOPin)),
302 (("eth", "MDC"), (quote!(crate::eth::MDCPin), quote!(#[cfg(feature="net")]))), 302 (("eth", "MDC"), quote!(crate::eth::MDCPin)),
303 (("eth", "CRS_DV"), (quote!(crate::eth::CRSPin), quote!(#[cfg(feature="net")]))), 303 (("eth", "CRS_DV"), quote!(crate::eth::CRSPin)),
304 (("eth", "RXD0"), (quote!(crate::eth::RXD0Pin), quote!(#[cfg(feature="net")]))), 304 (("eth", "RXD0"), quote!(crate::eth::RXD0Pin)),
305 (("eth", "RXD1"), (quote!(crate::eth::RXD1Pin), quote!(#[cfg(feature="net")]))), 305 (("eth", "RXD1"), quote!(crate::eth::RXD1Pin)),
306 (("eth", "TXD0"), (quote!(crate::eth::TXD0Pin), quote!(#[cfg(feature="net")]))), 306 (("eth", "TXD0"), quote!(crate::eth::TXD0Pin)),
307 (("eth", "TXD1"), (quote!(crate::eth::TXD1Pin), quote!(#[cfg(feature="net")]))), 307 (("eth", "TXD1"), quote!(crate::eth::TXD1Pin)),
308 (("eth", "TX_EN"), (quote!(crate::eth::TXEnPin), quote!(#[cfg(feature="net")]))), 308 (("eth", "TX_EN"), quote!(crate::eth::TXEnPin)),
309 (("fmc", "A0"), (quote!(crate::fmc::A0Pin), quote!())), 309 (("fmc", "A0"), quote!(crate::fmc::A0Pin)),
310 (("fmc", "A1"), (quote!(crate::fmc::A1Pin), quote!())), 310 (("fmc", "A1"), quote!(crate::fmc::A1Pin)),
311 (("fmc", "A2"), (quote!(crate::fmc::A2Pin), quote!())), 311 (("fmc", "A2"), quote!(crate::fmc::A2Pin)),
312 (("fmc", "A3"), (quote!(crate::fmc::A3Pin), quote!())), 312 (("fmc", "A3"), quote!(crate::fmc::A3Pin)),
313 (("fmc", "A4"), (quote!(crate::fmc::A4Pin), quote!())), 313 (("fmc", "A4"), quote!(crate::fmc::A4Pin)),
314 (("fmc", "A5"), (quote!(crate::fmc::A5Pin), quote!())), 314 (("fmc", "A5"), quote!(crate::fmc::A5Pin)),
315 (("fmc", "A6"), (quote!(crate::fmc::A6Pin), quote!())), 315 (("fmc", "A6"), quote!(crate::fmc::A6Pin)),
316 (("fmc", "A7"), (quote!(crate::fmc::A7Pin), quote!())), 316 (("fmc", "A7"), quote!(crate::fmc::A7Pin)),
317 (("fmc", "A8"), (quote!(crate::fmc::A8Pin), quote!())), 317 (("fmc", "A8"), quote!(crate::fmc::A8Pin)),
318 (("fmc", "A9"), (quote!(crate::fmc::A9Pin), quote!())), 318 (("fmc", "A9"), quote!(crate::fmc::A9Pin)),
319 (("fmc", "A10"), (quote!(crate::fmc::A10Pin), quote!())), 319 (("fmc", "A10"), quote!(crate::fmc::A10Pin)),
320 (("fmc", "A11"), (quote!(crate::fmc::A11Pin), quote!())), 320 (("fmc", "A11"), quote!(crate::fmc::A11Pin)),
321 (("fmc", "A12"), (quote!(crate::fmc::A12Pin), quote!())), 321 (("fmc", "A12"), quote!(crate::fmc::A12Pin)),
322 (("fmc", "A13"), (quote!(crate::fmc::A13Pin), quote!())), 322 (("fmc", "A13"), quote!(crate::fmc::A13Pin)),
323 (("fmc", "A14"), (quote!(crate::fmc::A14Pin), quote!())), 323 (("fmc", "A14"), quote!(crate::fmc::A14Pin)),
324 (("fmc", "A15"), (quote!(crate::fmc::A15Pin), quote!())), 324 (("fmc", "A15"), quote!(crate::fmc::A15Pin)),
325 (("fmc", "A16"), (quote!(crate::fmc::A16Pin), quote!())), 325 (("fmc", "A16"), quote!(crate::fmc::A16Pin)),
326 (("fmc", "A17"), (quote!(crate::fmc::A17Pin), quote!())), 326 (("fmc", "A17"), quote!(crate::fmc::A17Pin)),
327 (("fmc", "A18"), (quote!(crate::fmc::A18Pin), quote!())), 327 (("fmc", "A18"), quote!(crate::fmc::A18Pin)),
328 (("fmc", "A19"), (quote!(crate::fmc::A19Pin), quote!())), 328 (("fmc", "A19"), quote!(crate::fmc::A19Pin)),
329 (("fmc", "A20"), (quote!(crate::fmc::A20Pin), quote!())), 329 (("fmc", "A20"), quote!(crate::fmc::A20Pin)),
330 (("fmc", "A21"), (quote!(crate::fmc::A21Pin), quote!())), 330 (("fmc", "A21"), quote!(crate::fmc::A21Pin)),
331 (("fmc", "A22"), (quote!(crate::fmc::A22Pin), quote!())), 331 (("fmc", "A22"), quote!(crate::fmc::A22Pin)),
332 (("fmc", "A23"), (quote!(crate::fmc::A23Pin), quote!())), 332 (("fmc", "A23"), quote!(crate::fmc::A23Pin)),
333 (("fmc", "A24"), (quote!(crate::fmc::A24Pin), quote!())), 333 (("fmc", "A24"), quote!(crate::fmc::A24Pin)),
334 (("fmc", "A25"), (quote!(crate::fmc::A25Pin), quote!())), 334 (("fmc", "A25"), quote!(crate::fmc::A25Pin)),
335 (("fmc", "D0"), (quote!(crate::fmc::D0Pin), quote!())), 335 (("fmc", "D0"), quote!(crate::fmc::D0Pin)),
336 (("fmc", "D1"), (quote!(crate::fmc::D1Pin), quote!())), 336 (("fmc", "D1"), quote!(crate::fmc::D1Pin)),
337 (("fmc", "D2"), (quote!(crate::fmc::D2Pin), quote!())), 337 (("fmc", "D2"), quote!(crate::fmc::D2Pin)),
338 (("fmc", "D3"), (quote!(crate::fmc::D3Pin), quote!())), 338 (("fmc", "D3"), quote!(crate::fmc::D3Pin)),
339 (("fmc", "D4"), (quote!(crate::fmc::D4Pin), quote!())), 339 (("fmc", "D4"), quote!(crate::fmc::D4Pin)),
340 (("fmc", "D5"), (quote!(crate::fmc::D5Pin), quote!())), 340 (("fmc", "D5"), quote!(crate::fmc::D5Pin)),
341 (("fmc", "D6"), (quote!(crate::fmc::D6Pin), quote!())), 341 (("fmc", "D6"), quote!(crate::fmc::D6Pin)),
342 (("fmc", "D7"), (quote!(crate::fmc::D7Pin), quote!())), 342 (("fmc", "D7"), quote!(crate::fmc::D7Pin)),
343 (("fmc", "D8"), (quote!(crate::fmc::D8Pin), quote!())), 343 (("fmc", "D8"), quote!(crate::fmc::D8Pin)),
344 (("fmc", "D9"), (quote!(crate::fmc::D9Pin), quote!())), 344 (("fmc", "D9"), quote!(crate::fmc::D9Pin)),
345 (("fmc", "D10"), (quote!(crate::fmc::D10Pin), quote!())), 345 (("fmc", "D10"), quote!(crate::fmc::D10Pin)),
346 (("fmc", "D11"), (quote!(crate::fmc::D11Pin), quote!())), 346 (("fmc", "D11"), quote!(crate::fmc::D11Pin)),
347 (("fmc", "D12"), (quote!(crate::fmc::D12Pin), quote!())), 347 (("fmc", "D12"), quote!(crate::fmc::D12Pin)),
348 (("fmc", "D13"), (quote!(crate::fmc::D13Pin), quote!())), 348 (("fmc", "D13"), quote!(crate::fmc::D13Pin)),
349 (("fmc", "D14"), (quote!(crate::fmc::D14Pin), quote!())), 349 (("fmc", "D14"), quote!(crate::fmc::D14Pin)),
350 (("fmc", "D15"), (quote!(crate::fmc::D15Pin), quote!())), 350 (("fmc", "D15"), quote!(crate::fmc::D15Pin)),
351 (("fmc", "D16"), (quote!(crate::fmc::D16Pin), quote!())), 351 (("fmc", "D16"), quote!(crate::fmc::D16Pin)),
352 (("fmc", "D17"), (quote!(crate::fmc::D17Pin), quote!())), 352 (("fmc", "D17"), quote!(crate::fmc::D17Pin)),
353 (("fmc", "D18"), (quote!(crate::fmc::D18Pin), quote!())), 353 (("fmc", "D18"), quote!(crate::fmc::D18Pin)),
354 (("fmc", "D19"), (quote!(crate::fmc::D19Pin), quote!())), 354 (("fmc", "D19"), quote!(crate::fmc::D19Pin)),
355 (("fmc", "D20"), (quote!(crate::fmc::D20Pin), quote!())), 355 (("fmc", "D20"), quote!(crate::fmc::D20Pin)),
356 (("fmc", "D21"), (quote!(crate::fmc::D21Pin), quote!())), 356 (("fmc", "D21"), quote!(crate::fmc::D21Pin)),
357 (("fmc", "D22"), (quote!(crate::fmc::D22Pin), quote!())), 357 (("fmc", "D22"), quote!(crate::fmc::D22Pin)),
358 (("fmc", "D23"), (quote!(crate::fmc::D23Pin), quote!())), 358 (("fmc", "D23"), quote!(crate::fmc::D23Pin)),
359 (("fmc", "D24"), (quote!(crate::fmc::D24Pin), quote!())), 359 (("fmc", "D24"), quote!(crate::fmc::D24Pin)),
360 (("fmc", "D25"), (quote!(crate::fmc::D25Pin), quote!())), 360 (("fmc", "D25"), quote!(crate::fmc::D25Pin)),
361 (("fmc", "D26"), (quote!(crate::fmc::D26Pin), quote!())), 361 (("fmc", "D26"), quote!(crate::fmc::D26Pin)),
362 (("fmc", "D27"), (quote!(crate::fmc::D27Pin), quote!())), 362 (("fmc", "D27"), quote!(crate::fmc::D27Pin)),
363 (("fmc", "D28"), (quote!(crate::fmc::D28Pin), quote!())), 363 (("fmc", "D28"), quote!(crate::fmc::D28Pin)),
364 (("fmc", "D29"), (quote!(crate::fmc::D29Pin), quote!())), 364 (("fmc", "D29"), quote!(crate::fmc::D29Pin)),
365 (("fmc", "D30"), (quote!(crate::fmc::D30Pin), quote!())), 365 (("fmc", "D30"), quote!(crate::fmc::D30Pin)),
366 (("fmc", "D31"), (quote!(crate::fmc::D31Pin), quote!())), 366 (("fmc", "D31"), quote!(crate::fmc::D31Pin)),
367 (("fmc", "DA0"), (quote!(crate::fmc::DA0Pin), quote!())), 367 (("fmc", "DA0"), quote!(crate::fmc::DA0Pin)),
368 (("fmc", "DA1"), (quote!(crate::fmc::DA1Pin), quote!())), 368 (("fmc", "DA1"), quote!(crate::fmc::DA1Pin)),
369 (("fmc", "DA2"), (quote!(crate::fmc::DA2Pin), quote!())), 369 (("fmc", "DA2"), quote!(crate::fmc::DA2Pin)),
370 (("fmc", "DA3"), (quote!(crate::fmc::DA3Pin), quote!())), 370 (("fmc", "DA3"), quote!(crate::fmc::DA3Pin)),
371 (("fmc", "DA4"), (quote!(crate::fmc::DA4Pin), quote!())), 371 (("fmc", "DA4"), quote!(crate::fmc::DA4Pin)),
372 (("fmc", "DA5"), (quote!(crate::fmc::DA5Pin), quote!())), 372 (("fmc", "DA5"), quote!(crate::fmc::DA5Pin)),
373 (("fmc", "DA6"), (quote!(crate::fmc::DA6Pin), quote!())), 373 (("fmc", "DA6"), quote!(crate::fmc::DA6Pin)),
374 (("fmc", "DA7"), (quote!(crate::fmc::DA7Pin), quote!())), 374 (("fmc", "DA7"), quote!(crate::fmc::DA7Pin)),
375 (("fmc", "DA8"), (quote!(crate::fmc::DA8Pin), quote!())), 375 (("fmc", "DA8"), quote!(crate::fmc::DA8Pin)),
376 (("fmc", "DA9"), (quote!(crate::fmc::DA9Pin), quote!())), 376 (("fmc", "DA9"), quote!(crate::fmc::DA9Pin)),
377 (("fmc", "DA10"), (quote!(crate::fmc::DA10Pin), quote!())), 377 (("fmc", "DA10"), quote!(crate::fmc::DA10Pin)),
378 (("fmc", "DA11"), (quote!(crate::fmc::DA11Pin), quote!())), 378 (("fmc", "DA11"), quote!(crate::fmc::DA11Pin)),
379 (("fmc", "DA12"), (quote!(crate::fmc::DA12Pin), quote!())), 379 (("fmc", "DA12"), quote!(crate::fmc::DA12Pin)),
380 (("fmc", "DA13"), (quote!(crate::fmc::DA13Pin), quote!())), 380 (("fmc", "DA13"), quote!(crate::fmc::DA13Pin)),
381 (("fmc", "DA14"), (quote!(crate::fmc::DA14Pin), quote!())), 381 (("fmc", "DA14"), quote!(crate::fmc::DA14Pin)),
382 (("fmc", "DA15"), (quote!(crate::fmc::DA15Pin), quote!())), 382 (("fmc", "DA15"), quote!(crate::fmc::DA15Pin)),
383 (("fmc", "SDNWE"), (quote!(crate::fmc::SDNWEPin), quote!())), 383 (("fmc", "SDNWE"), quote!(crate::fmc::SDNWEPin)),
384 (("fmc", "SDNCAS"), (quote!(crate::fmc::SDNCASPin), quote!())), 384 (("fmc", "SDNCAS"), quote!(crate::fmc::SDNCASPin)),
385 (("fmc", "SDNRAS"), (quote!(crate::fmc::SDNRASPin), quote!())), 385 (("fmc", "SDNRAS"), quote!(crate::fmc::SDNRASPin)),
386 (("fmc", "SDNE0"), (quote!(crate::fmc::SDNE0Pin), quote!())), 386 (("fmc", "SDNE0"), quote!(crate::fmc::SDNE0Pin)),
387 (("fmc", "SDNE1"), (quote!(crate::fmc::SDNE1Pin), quote!())), 387 (("fmc", "SDNE1"), quote!(crate::fmc::SDNE1Pin)),
388 (("fmc", "SDCKE0"), (quote!(crate::fmc::SDCKE0Pin), quote!())), 388 (("fmc", "SDCKE0"), quote!(crate::fmc::SDCKE0Pin)),
389 (("fmc", "SDCKE1"), (quote!(crate::fmc::SDCKE1Pin), quote!())), 389 (("fmc", "SDCKE1"), quote!(crate::fmc::SDCKE1Pin)),
390 (("fmc", "SDCLK"), (quote!(crate::fmc::SDCLKPin), quote!())), 390 (("fmc", "SDCLK"), quote!(crate::fmc::SDCLKPin)),
391 (("fmc", "NBL0"), (quote!(crate::fmc::NBL0Pin), quote!())), 391 (("fmc", "NBL0"), quote!(crate::fmc::NBL0Pin)),
392 (("fmc", "NBL1"), (quote!(crate::fmc::NBL1Pin), quote!())), 392 (("fmc", "NBL1"), quote!(crate::fmc::NBL1Pin)),
393 (("fmc", "NBL2"), (quote!(crate::fmc::NBL2Pin), quote!())), 393 (("fmc", "NBL2"), quote!(crate::fmc::NBL2Pin)),
394 (("fmc", "NBL3"), (quote!(crate::fmc::NBL3Pin), quote!())), 394 (("fmc", "NBL3"), quote!(crate::fmc::NBL3Pin)),
395 (("fmc", "INT"), (quote!(crate::fmc::INTPin), quote!())), 395 (("fmc", "INT"), quote!(crate::fmc::INTPin)),
396 (("fmc", "NL"), (quote!(crate::fmc::NLPin), quote!())), 396 (("fmc", "NL"), quote!(crate::fmc::NLPin)),
397 (("fmc", "NWAIT"), (quote!(crate::fmc::NWaitPin), quote!())), 397 (("fmc", "NWAIT"), quote!(crate::fmc::NWaitPin)),
398 (("fmc", "NE1"), (quote!(crate::fmc::NE1Pin), quote!())), 398 (("fmc", "NE1"), quote!(crate::fmc::NE1Pin)),
399 (("fmc", "NE2"), (quote!(crate::fmc::NE2Pin), quote!())), 399 (("fmc", "NE2"), quote!(crate::fmc::NE2Pin)),
400 (("fmc", "NE3"), (quote!(crate::fmc::NE3Pin), quote!())), 400 (("fmc", "NE3"), quote!(crate::fmc::NE3Pin)),
401 (("fmc", "NE4"), (quote!(crate::fmc::NE4Pin), quote!())), 401 (("fmc", "NE4"), quote!(crate::fmc::NE4Pin)),
402 (("fmc", "NCE"), (quote!(crate::fmc::NCEPin), quote!())), 402 (("fmc", "NCE"), quote!(crate::fmc::NCEPin)),
403 (("fmc", "NOE"), (quote!(crate::fmc::NOEPin), quote!())), 403 (("fmc", "NOE"), quote!(crate::fmc::NOEPin)),
404 (("fmc", "NWE"), (quote!(crate::fmc::NWEPin), quote!())), 404 (("fmc", "NWE"), quote!(crate::fmc::NWEPin)),
405 (("fmc", "Clk"), (quote!(crate::fmc::ClkPin), quote!())), 405 (("fmc", "Clk"), quote!(crate::fmc::ClkPin)),
406 (("fmc", "BA0"), (quote!(crate::fmc::BA0Pin), quote!())), 406 (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)),
407 (("fmc", "BA1"), (quote!(crate::fmc::BA1Pin), quote!())), 407 (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)),
408 (("timer", "CH1"), (quote!(crate::pwm::Channel1Pin), quote!())), 408 (("timer", "CH1"), quote!(crate::pwm::Channel1Pin)),
409 (("timer", "CH1N"), (quote!(crate::pwm::Channel1ComplementaryPin), quote!())), 409 (("timer", "CH1N"), quote!(crate::pwm::Channel1ComplementaryPin)),
410 (("timer", "CH2"), (quote!(crate::pwm::Channel2Pin), quote!())), 410 (("timer", "CH2"), quote!(crate::pwm::Channel2Pin)),
411 (("timer", "CH2N"), (quote!(crate::pwm::Channel2ComplementaryPin), quote!())), 411 (("timer", "CH2N"), quote!(crate::pwm::Channel2ComplementaryPin)),
412 (("timer", "CH3"), (quote!(crate::pwm::Channel3Pin), quote!())), 412 (("timer", "CH3"), quote!(crate::pwm::Channel3Pin)),
413 (("timer", "CH3N"), (quote!(crate::pwm::Channel3ComplementaryPin), quote!())), 413 (("timer", "CH3N"), quote!(crate::pwm::Channel3ComplementaryPin)),
414 (("timer", "CH4"), (quote!(crate::pwm::Channel4Pin), quote!())), 414 (("timer", "CH4"), quote!(crate::pwm::Channel4Pin)),
415 (("timer", "CH4N"), (quote!(crate::pwm::Channel4ComplementaryPin), quote!())), 415 (("timer", "CH4N"), quote!(crate::pwm::Channel4ComplementaryPin)),
416 (("timer", "ETR"), (quote!(crate::pwm::ExternalTriggerPin), quote!())), 416 (("timer", "ETR"), quote!(crate::pwm::ExternalTriggerPin)),
417 (("timer", "BKIN"), (quote!(crate::pwm::BreakInputPin), quote!())), 417 (("timer", "BKIN"), quote!(crate::pwm::BreakInputPin)),
418 (("timer", "BKIN_COMP1"), (quote!(crate::pwm::BreakInputComparator1Pin), quote!())), 418 (("timer", "BKIN_COMP1"), quote!(crate::pwm::BreakInputComparator1Pin)),
419 (("timer", "BKIN_COMP2"), (quote!(crate::pwm::BreakInputComparator2Pin), quote!())), 419 (("timer", "BKIN_COMP2"), quote!(crate::pwm::BreakInputComparator2Pin)),
420 (("timer", "BKIN2"), (quote!(crate::pwm::BreakInput2Pin), quote!())), 420 (("timer", "BKIN2"), quote!(crate::pwm::BreakInput2Pin)),
421 (("timer", "BKIN2_COMP1"), (quote!(crate::pwm::BreakInput2Comparator1Pin), quote!())), 421 (("timer", "BKIN2_COMP1"), quote!(crate::pwm::BreakInput2Comparator1Pin)),
422 (("timer", "BKIN2_COMP2"), (quote!(crate::pwm::BreakInput2Comparator2Pin), quote!())), 422 (("timer", "BKIN2_COMP2"), quote!(crate::pwm::BreakInput2Comparator2Pin)),
423 (("sdmmc", "CK"), (quote!(crate::sdmmc::CkPin), quote!())), 423 (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)),
424 (("sdmmc", "CMD"), (quote!(crate::sdmmc::CmdPin), quote!())), 424 (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)),
425 (("sdmmc", "D0"), (quote!(crate::sdmmc::D0Pin), quote!())), 425 (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)),
426 (("sdmmc", "D1"), (quote!(crate::sdmmc::D1Pin), quote!())), 426 (("sdmmc", "D1"), quote!(crate::sdmmc::D1Pin)),
427 (("sdmmc", "D2"), (quote!(crate::sdmmc::D2Pin), quote!())), 427 (("sdmmc", "D2"), quote!(crate::sdmmc::D2Pin)),
428 (("sdmmc", "D3"), (quote!(crate::sdmmc::D3Pin), quote!())), 428 (("sdmmc", "D3"), quote!(crate::sdmmc::D3Pin)),
429 (("sdmmc", "D4"), (quote!(crate::sdmmc::D4Pin), quote!())), 429 (("sdmmc", "D4"), quote!(crate::sdmmc::D4Pin)),
430 (("sdmmc", "D5"), (quote!(crate::sdmmc::D5Pin), quote!())), 430 (("sdmmc", "D5"), quote!(crate::sdmmc::D5Pin)),
431 (("sdmmc", "D6"), (quote!(crate::sdmmc::D6Pin), quote!())), 431 (("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)),
432 (("sdmmc", "D6"), (quote!(crate::sdmmc::D7Pin), quote!())), 432 (("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)),
433 (("sdmmc", "D8"), (quote!(crate::sdmmc::D8Pin), quote!())), 433 (("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)),
434 ].into(); 434 ].into();
435 435
436 for p in METADATA.peripherals { 436 for p in METADATA.peripherals {
437 if let Some(regs) = &p.registers { 437 if let Some(regs) = &p.registers {
438 for pin in p.pins { 438 for pin in p.pins {
439 let key = (regs.kind, pin.signal); 439 let key = (regs.kind, pin.signal);
440 if let Some((tr, cfgs)) = signals.get(&key) { 440 if let Some(tr) = signals.get(&key) {
441 let mut peri = format_ident!("{}", p.name); 441 let mut peri = format_ident!("{}", p.name);
442 let pin_name = format_ident!("{}", pin.pin); 442 let pin_name = format_ident!("{}", pin.pin);
443 let af = pin.af.unwrap_or(0); 443 let af = pin.af.unwrap_or(0);
@@ -453,7 +453,6 @@ fn main() {
453 } 453 }
454 454
455 g.extend(quote! { 455 g.extend(quote! {
456 #cfgs
457 pin_trait_impl!(#tr, #peri, #pin_name, #af); 456 pin_trait_impl!(#tr, #peri, #pin_name, #af);
458 }) 457 })
459 } 458 }
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs
index 28f0c178f..76a3dfab4 100644
--- a/embassy-stm32/src/eth/mod.rs
+++ b/embassy-stm32/src/eth/mod.rs
@@ -1,10 +1,12 @@
1#![macro_use] 1#![macro_use]
2 2
3#[cfg(feature = "net")]
3#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] 4#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")]
4#[cfg_attr(eth_v2, path = "v2/mod.rs")] 5#[cfg_attr(eth_v2, path = "v2/mod.rs")]
5mod _version; 6mod _version;
6pub mod generic_smi; 7pub mod generic_smi;
7 8
9#[cfg(feature = "net")]
8pub use _version::*; 10pub use _version::*;
9 11
10/// Station Management Interface (SMI) on an ethernet PHY 12/// Station Management Interface (SMI) on an ethernet PHY
diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs
new file mode 100644
index 000000000..a5dc8dd0a
--- /dev/null
+++ b/embassy-stm32/src/flash/f3.rs
@@ -0,0 +1,104 @@
1use core::convert::TryInto;
2use core::ptr::write_volatile;
3
4use crate::flash::Error;
5use crate::pac;
6
7pub(crate) unsafe fn lock() {
8 pac::FLASH.cr().modify(|w| w.set_lock(true));
9}
10
11pub(crate) unsafe fn unlock() {
12 pac::FLASH.keyr().write(|w| w.set_fkeyr(0x4567_0123));
13 pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB));
14}
15
16pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
17 pac::FLASH.cr().write(|w| w.set_pg(true));
18
19 let ret = {
20 let mut ret: Result<(), Error> = Ok(());
21 let mut offset = offset;
22 for chunk in buf.chunks(2) {
23 write_volatile(
24 offset as *mut u16,
25 u16::from_le_bytes(chunk[0..2].try_into().unwrap()),
26 );
27 offset += chunk.len() as u32;
28
29 ret = blocking_wait_ready();
30 if ret.is_err() {
31 break;
32 }
33 }
34 ret
35 };
36
37 pac::FLASH.cr().write(|w| w.set_pg(false));
38
39 ret
40}
41
42pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
43 for page in (from..to).step_by(super::ERASE_SIZE) {
44 pac::FLASH.cr().modify(|w| {
45 w.set_per(true);
46 });
47
48 pac::FLASH.ar().write(|w| w.set_far(page));
49
50 pac::FLASH.cr().modify(|w| {
51 w.set_strt(true);
52 });
53
54 let mut ret: Result<(), Error> = blocking_wait_ready();
55
56 if !pac::FLASH.sr().read().eop() {
57 trace!("FLASH: EOP not set");
58 ret = Err(Error::Prog);
59 } else {
60 pac::FLASH.sr().write(|w| w.set_eop(true));
61 }
62
63 pac::FLASH.cr().modify(|w| w.set_per(false));
64
65 clear_all_err();
66 if ret.is_err() {
67 return ret;
68 }
69 }
70
71 Ok(())
72}
73
74pub(crate) unsafe fn clear_all_err() {
75 pac::FLASH.sr().modify(|w| {
76 if w.pgerr() {
77 w.set_pgerr(true);
78 }
79 if w.wrprterr() {
80 w.set_wrprterr(true);
81 }
82 if w.eop() {
83 w.set_eop(true);
84 }
85 });
86}
87
88pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
89 loop {
90 let sr = pac::FLASH.sr().read();
91
92 if !sr.bsy() {
93 if sr.wrprterr() {
94 return Err(Error::Protected);
95 }
96
97 if sr.pgerr() {
98 return Err(Error::Seq);
99 }
100
101 return Ok(());
102 }
103 }
104}
diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs
new file mode 100644
index 000000000..16316fd93
--- /dev/null
+++ b/embassy-stm32/src/flash/f7.rs
@@ -0,0 +1,138 @@
1use core::convert::TryInto;
2use core::ptr::write_volatile;
3
4use atomic_polyfill::{fence, Ordering};
5
6use crate::flash::Error;
7use crate::pac;
8
9pub(crate) unsafe fn lock() {
10 pac::FLASH.cr().modify(|w| w.set_lock(true));
11}
12
13pub(crate) unsafe fn unlock() {
14 pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123));
15 pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
16}
17
18pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
19 pac::FLASH.cr().write(|w| {
20 w.set_pg(true);
21 w.set_psize(pac::flash::vals::Psize::PSIZE32);
22 });
23
24 let ret = {
25 let mut ret: Result<(), Error> = Ok(());
26 let mut offset = offset;
27 for chunk in buf.chunks(super::WRITE_SIZE) {
28 for val in chunk.chunks(4) {
29 write_volatile(
30 offset as *mut u32,
31 u32::from_le_bytes(val[0..4].try_into().unwrap()),
32 );
33 offset += val.len() as u32;
34
35 // prevents parallelism errors
36 fence(Ordering::SeqCst);
37 }
38
39 ret = blocking_wait_ready();
40 if ret.is_err() {
41 break;
42 }
43 }
44 ret
45 };
46
47 pac::FLASH.cr().write(|w| w.set_pg(false));
48
49 ret
50}
51
52pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
53 let start_sector = if from >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 {
54 4 + (from - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32
55 } else {
56 (from - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8)
57 };
58
59 let end_sector = if to >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 {
60 4 + (to - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32
61 } else {
62 (to - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8)
63 };
64
65 for sector in start_sector..end_sector {
66 let ret = erase_sector(sector as u8);
67 if ret.is_err() {
68 return ret;
69 }
70 }
71
72 Ok(())
73}
74
75unsafe fn erase_sector(sector: u8) -> Result<(), Error> {
76 pac::FLASH.cr().modify(|w| {
77 w.set_ser(true);
78 w.set_snb(sector)
79 });
80
81 pac::FLASH.cr().modify(|w| {
82 w.set_strt(true);
83 });
84
85 let ret: Result<(), Error> = blocking_wait_ready();
86
87 pac::FLASH.cr().modify(|w| w.set_ser(false));
88
89 clear_all_err();
90
91 ret
92}
93
94pub(crate) unsafe fn clear_all_err() {
95 pac::FLASH.sr().modify(|w| {
96 if w.erserr() {
97 w.set_erserr(true);
98 }
99 if w.pgperr() {
100 w.set_pgperr(true);
101 }
102 if w.pgaerr() {
103 w.set_pgaerr(true);
104 }
105 if w.wrperr() {
106 w.set_wrperr(true);
107 }
108 if w.eop() {
109 w.set_eop(true);
110 }
111 });
112}
113
114pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
115 loop {
116 let sr = pac::FLASH.sr().read();
117
118 if !sr.bsy() {
119 if sr.erserr() {
120 return Err(Error::Seq);
121 }
122
123 if sr.pgperr() {
124 return Err(Error::Parallelism);
125 }
126
127 if sr.pgaerr() {
128 return Err(Error::Unaligned);
129 }
130
131 if sr.wrperr() {
132 return Err(Error::Protected);
133 }
134
135 return Ok(());
136 }
137 }
138}
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs
new file mode 100644
index 000000000..afccffc4a
--- /dev/null
+++ b/embassy-stm32/src/flash/h7.rs
@@ -0,0 +1,202 @@
1use core::convert::TryInto;
2use core::ptr::write_volatile;
3
4use crate::flash::Error;
5use crate::pac;
6
7const SECOND_BANK_OFFSET: usize = 0x0010_0000;
8
9const fn is_dual_bank() -> bool {
10 super::FLASH_SIZE / 2 > super::ERASE_SIZE
11}
12
13pub(crate) unsafe fn lock() {
14 pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true));
15 if is_dual_bank() {
16 pac::FLASH.bank(1).cr().modify(|w| w.set_lock(true));
17 }
18}
19
20pub(crate) unsafe fn unlock() {
21 pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123));
22 pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
23
24 if is_dual_bank() {
25 pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123));
26 pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
27 }
28}
29
30pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
31 let bank = if !is_dual_bank() || (offset - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32
32 {
33 pac::FLASH.bank(0)
34 } else {
35 pac::FLASH.bank(1)
36 };
37
38 bank.cr().write(|w| {
39 w.set_pg(true);
40 w.set_psize(2); // 32 bits at once
41 });
42
43 let ret = {
44 let mut ret: Result<(), Error> = Ok(());
45 let mut offset = offset;
46 'outer: for chunk in buf.chunks(super::WRITE_SIZE) {
47 for val in chunk.chunks(4) {
48 trace!("Writing at {:x}", offset);
49 write_volatile(
50 offset as *mut u32,
51 u32::from_le_bytes(val[0..4].try_into().unwrap()),
52 );
53 offset += val.len() as u32;
54
55 ret = blocking_wait_ready(bank);
56 bank.sr().modify(|w| {
57 if w.eop() {
58 w.set_eop(true);
59 }
60 });
61 if ret.is_err() {
62 break 'outer;
63 }
64 }
65 }
66 ret
67 };
68
69 bank.cr().write(|w| w.set_pg(false));
70
71 ret
72}
73
74pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
75 let from = from - super::FLASH_BASE as u32;
76 let to = to - super::FLASH_BASE as u32;
77
78 let bank_size = (super::FLASH_SIZE / 2) as u32;
79
80 let (bank, start, end) = if to <= bank_size {
81 let start_sector = from / super::ERASE_SIZE as u32;
82 let end_sector = to / super::ERASE_SIZE as u32;
83 (0, start_sector, end_sector)
84 } else if from >= SECOND_BANK_OFFSET as u32 && to <= (SECOND_BANK_OFFSET as u32 + bank_size) {
85 let start_sector = (from - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32;
86 let end_sector = (to - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32;
87 (1, start_sector, end_sector)
88 } else {
89 error!("Attempting to write outside of defined sectors");
90 return Err(Error::Unaligned);
91 };
92
93 trace!("Erasing bank {}, sectors from {} to {}", bank, start, end);
94 for sector in start..end {
95 let ret = erase_sector(pac::FLASH.bank(bank), sector as u8);
96 if ret.is_err() {
97 return ret;
98 }
99 }
100
101 Ok(())
102}
103
104unsafe fn erase_sector(bank: pac::flash::Bank, sector: u8) -> Result<(), Error> {
105 bank.cr().modify(|w| {
106 w.set_ser(true);
107 w.set_snb(sector)
108 });
109
110 bank.cr().modify(|w| {
111 w.set_start(true);
112 });
113
114 let ret: Result<(), Error> = blocking_wait_ready(bank);
115
116 bank.cr().modify(|w| w.set_ser(false));
117
118 bank_clear_all_err(bank);
119
120 ret
121}
122
123pub(crate) unsafe fn clear_all_err() {
124 bank_clear_all_err(pac::FLASH.bank(0));
125 bank_clear_all_err(pac::FLASH.bank(1));
126}
127
128unsafe fn bank_clear_all_err(bank: pac::flash::Bank) {
129 bank.sr().modify(|w| {
130 if w.wrperr() {
131 w.set_wrperr(true);
132 }
133 if w.pgserr() {
134 w.set_pgserr(true);
135 }
136 if w.strberr() {
137 // single address was written multiple times, can be ignored
138 w.set_strberr(true);
139 }
140 if w.incerr() {
141 // writing to a different address when programming 256 bit word was not finished
142 w.set_incerr(true);
143 }
144 if w.operr() {
145 w.set_operr(true);
146 }
147 if w.sneccerr1() {
148 // single ECC error
149 w.set_sneccerr1(true);
150 }
151 if w.dbeccerr() {
152 // double ECC error
153 w.set_dbeccerr(true);
154 }
155 if w.rdperr() {
156 w.set_rdperr(true);
157 }
158 if w.rdserr() {
159 w.set_rdserr(true);
160 }
161 });
162}
163
164pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
165 loop {
166 let sr = bank.sr().read();
167
168 if !sr.bsy() && !sr.qw() {
169 if sr.wrperr() {
170 return Err(Error::Protected);
171 }
172 if sr.pgserr() {
173 error!("pgserr");
174 return Err(Error::Seq);
175 }
176 if sr.incerr() {
177 // writing to a different address when programming 256 bit word was not finished
178 error!("incerr");
179 return Err(Error::Seq);
180 }
181 if sr.operr() {
182 return Err(Error::Prog);
183 }
184 if sr.sneccerr1() {
185 // single ECC error
186 return Err(Error::Prog);
187 }
188 if sr.dbeccerr() {
189 // double ECC error
190 return Err(Error::Prog);
191 }
192 if sr.rdperr() {
193 return Err(Error::Protected);
194 }
195 if sr.rdserr() {
196 return Err(Error::Protected);
197 }
198
199 return Ok(());
200 }
201 }
202}
diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs
new file mode 100644
index 000000000..6ec4796a5
--- /dev/null
+++ b/embassy-stm32/src/flash/l.rs
@@ -0,0 +1,185 @@
1use core::convert::TryInto;
2use core::ptr::write_volatile;
3
4use crate::flash::Error;
5use crate::pac;
6
7pub(crate) unsafe fn lock() {
8 #[cfg(any(flash_wl, flash_wb, flash_l4))]
9 pac::FLASH.cr().modify(|w| w.set_lock(true));
10
11 #[cfg(any(flash_l0))]
12 pac::FLASH.pecr().modify(|w| {
13 w.set_optlock(true);
14 w.set_prglock(true);
15 w.set_pelock(true);
16 });
17}
18
19pub(crate) unsafe fn unlock() {
20 #[cfg(any(flash_wl, flash_wb, flash_l4))]
21 {
22 pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
23 pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
24 }
25
26 #[cfg(any(flash_l0, flash_l1))]
27 {
28 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x89ABCDEF));
29 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x02030405));
30
31 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x8C9DAEBF));
32 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x13141516));
33 }
34}
35
36pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
37 #[cfg(any(flash_wl, flash_wb, flash_l4))]
38 pac::FLASH.cr().write(|w| w.set_pg(true));
39
40 let ret = {
41 let mut ret: Result<(), Error> = Ok(());
42 let mut offset = offset;
43 for chunk in buf.chunks(super::WRITE_SIZE) {
44 for val in chunk.chunks(4) {
45 write_volatile(
46 offset as *mut u32,
47 u32::from_le_bytes(val[0..4].try_into().unwrap()),
48 );
49 offset += val.len() as u32;
50 }
51
52 ret = blocking_wait_ready();
53 if ret.is_err() {
54 break;
55 }
56 }
57 ret
58 };
59
60 #[cfg(any(flash_wl, flash_wb, flash_l4))]
61 pac::FLASH.cr().write(|w| w.set_pg(false));
62
63 ret
64}
65
66pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
67 for page in (from..to).step_by(super::ERASE_SIZE) {
68 #[cfg(any(flash_l0, flash_l1))]
69 {
70 pac::FLASH.pecr().modify(|w| {
71 w.set_erase(true);
72 w.set_prog(true);
73 });
74
75 write_volatile(page as *mut u32, 0xFFFFFFFF);
76 }
77
78 #[cfg(any(flash_wl, flash_wb, flash_l4))]
79 {
80 let idx = page / super::ERASE_SIZE as u32;
81
82 pac::FLASH.cr().modify(|w| {
83 w.set_per(true);
84 w.set_pnb(idx as u8);
85 #[cfg(any(flash_wl, flash_wb))]
86 w.set_strt(true);
87 #[cfg(any(flash_l4))]
88 w.set_start(true);
89 });
90 }
91
92 let ret: Result<(), Error> = blocking_wait_ready();
93
94 #[cfg(any(flash_wl, flash_wb, flash_l4))]
95 pac::FLASH.cr().modify(|w| w.set_per(false));
96
97 #[cfg(any(flash_l0, flash_l1))]
98 pac::FLASH.pecr().modify(|w| {
99 w.set_erase(false);
100 w.set_prog(false);
101 });
102
103 clear_all_err();
104 if ret.is_err() {
105 return ret;
106 }
107 }
108
109 Ok(())
110}
111
112pub(crate) unsafe fn clear_all_err() {
113 pac::FLASH.sr().modify(|w| {
114 #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l0))]
115 if w.rderr() {
116 w.set_rderr(true);
117 }
118 #[cfg(any(flash_wl, flash_wb, flash_l4))]
119 if w.fasterr() {
120 w.set_fasterr(true);
121 }
122 #[cfg(any(flash_wl, flash_wb, flash_l4))]
123 if w.miserr() {
124 w.set_miserr(true);
125 }
126 #[cfg(any(flash_wl, flash_wb, flash_l4))]
127 if w.pgserr() {
128 w.set_pgserr(true);
129 }
130 if w.sizerr() {
131 w.set_sizerr(true);
132 }
133 if w.pgaerr() {
134 w.set_pgaerr(true);
135 }
136 if w.wrperr() {
137 w.set_wrperr(true);
138 }
139 #[cfg(any(flash_wl, flash_wb, flash_l4))]
140 if w.progerr() {
141 w.set_progerr(true);
142 }
143 #[cfg(any(flash_wl, flash_wb, flash_l4))]
144 if w.operr() {
145 w.set_operr(true);
146 }
147 });
148}
149
150pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
151 loop {
152 let sr = pac::FLASH.sr().read();
153
154 if !sr.bsy() {
155 #[cfg(any(flash_wl, flash_wb, flash_l4))]
156 if sr.progerr() {
157 return Err(Error::Prog);
158 }
159
160 if sr.wrperr() {
161 return Err(Error::Protected);
162 }
163
164 if sr.pgaerr() {
165 return Err(Error::Unaligned);
166 }
167
168 if sr.sizerr() {
169 return Err(Error::Size);
170 }
171
172 #[cfg(any(flash_wl, flash_wb, flash_l4))]
173 if sr.miserr() {
174 return Err(Error::Miss);
175 }
176
177 #[cfg(any(flash_wl, flash_wb, flash_l4))]
178 if sr.pgserr() {
179 return Err(Error::Seq);
180 }
181
182 return Ok(());
183 }
184 }
185}
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index cff3119fd..4be611d2e 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -1,8 +1,5 @@
1use crate::pac;
2use crate::peripherals::FLASH; 1use crate::peripherals::FLASH;
3use core::convert::TryInto;
4use core::marker::PhantomData; 2use core::marker::PhantomData;
5use core::ptr::write_volatile;
6use embassy::util::Unborrow; 3use embassy::util::Unborrow;
7use embassy_hal_common::unborrow; 4use embassy_hal_common::unborrow;
8 5
@@ -17,6 +14,12 @@ pub use crate::pac::FLASH_SIZE;
17pub use crate::pac::WRITE_SIZE; 14pub use crate::pac::WRITE_SIZE;
18const FLASH_END: usize = FLASH_BASE + FLASH_SIZE; 15const FLASH_END: usize = FLASH_BASE + FLASH_SIZE;
19 16
17#[cfg_attr(any(flash_wl, flash_wb, flash_l0, flash_l1, flash_l4), path = "l.rs")]
18#[cfg_attr(flash_f3, path = "f3.rs")]
19#[cfg_attr(flash_f7, path = "f7.rs")]
20#[cfg_attr(flash_h7, path = "h7.rs")]
21mod family;
22
20pub struct Flash<'d> { 23pub struct Flash<'d> {
21 _inner: FLASH, 24 _inner: FLASH,
22 _phantom: PhantomData<&'d mut FLASH>, 25 _phantom: PhantomData<&'d mut FLASH>,
@@ -33,37 +36,13 @@ impl<'d> Flash<'d> {
33 36
34 pub fn unlock(p: impl Unborrow<Target = FLASH>) -> Self { 37 pub fn unlock(p: impl Unborrow<Target = FLASH>) -> Self {
35 let flash = Self::new(p); 38 let flash = Self::new(p);
36 #[cfg(any(flash_wl, flash_wb, flash_l4))]
37 unsafe {
38 pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
39 pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
40 }
41 39
42 #[cfg(any(flash_l0))] 40 unsafe { family::unlock() };
43 unsafe {
44 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x89ABCDEF));
45 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x02030405));
46
47 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x8C9DAEBF));
48 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x13141516));
49 }
50 flash 41 flash
51 } 42 }
52 43
53 pub fn lock(&mut self) { 44 pub fn lock(&mut self) {
54 #[cfg(any(flash_wl, flash_wb, flash_l4))] 45 unsafe { family::lock() };
55 unsafe {
56 pac::FLASH.cr().modify(|w| w.set_lock(true));
57 }
58
59 #[cfg(any(flash_l0))]
60 unsafe {
61 pac::FLASH.pecr().modify(|w| {
62 w.set_optlock(true);
63 w.set_prglock(true);
64 w.set_pelock(true);
65 });
66 }
67 } 46 }
68 47
69 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { 48 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
@@ -89,36 +68,7 @@ impl<'d> Flash<'d> {
89 68
90 self.clear_all_err(); 69 self.clear_all_err();
91 70
92 #[cfg(any(flash_wl, flash_wb, flash_l4))] 71 unsafe { family::blocking_write(offset, buf) }
93 unsafe {
94 pac::FLASH.cr().write(|w| w.set_pg(true))
95 }
96
97 let mut ret: Result<(), Error> = Ok(());
98 let mut offset = offset;
99 for chunk in buf.chunks(WRITE_SIZE) {
100 for val in chunk.chunks(4) {
101 unsafe {
102 write_volatile(
103 offset as *mut u32,
104 u32::from_le_bytes(val[0..4].try_into().unwrap()),
105 );
106 }
107 offset += val.len() as u32;
108 }
109
110 ret = self.blocking_wait_ready();
111 if ret.is_err() {
112 break;
113 }
114 }
115
116 #[cfg(any(flash_wl, flash_wb, flash_l4))]
117 unsafe {
118 pac::FLASH.cr().write(|w| w.set_pg(false))
119 }
120
121 ret
122 } 72 }
123 73
124 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { 74 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
@@ -127,135 +77,17 @@ impl<'d> Flash<'d> {
127 if to < from || to as usize > FLASH_END { 77 if to < from || to as usize > FLASH_END {
128 return Err(Error::Size); 78 return Err(Error::Size);
129 } 79 }
130 if from as usize % ERASE_SIZE != 0 || to as usize % ERASE_SIZE != 0 { 80 if ((to - from) as usize % ERASE_SIZE) != 0 {
131 return Err(Error::Unaligned); 81 return Err(Error::Unaligned);
132 } 82 }
133 83
134 self.clear_all_err(); 84 self.clear_all_err();
135 85
136 for page in (from..to).step_by(ERASE_SIZE) { 86 unsafe { family::blocking_erase(from, to) }
137 #[cfg(any(flash_l0, flash_l1))]
138 unsafe {
139 pac::FLASH.pecr().modify(|w| {
140 w.set_erase(true);
141 w.set_prog(true);
142 });
143
144 write_volatile(page as *mut u32, 0xFFFFFFFF);
145 }
146
147 #[cfg(any(flash_wl, flash_wb, flash_l4))]
148 unsafe {
149 let idx = page / ERASE_SIZE as u32;
150
151 pac::FLASH.cr().modify(|w| {
152 w.set_per(true);
153 w.set_pnb(idx as u8);
154 #[cfg(any(flash_wl, flash_wb))]
155 w.set_strt(true);
156 #[cfg(any(flash_l4))]
157 w.set_start(true);
158 });
159 }
160
161 let ret: Result<(), Error> = self.blocking_wait_ready();
162
163 #[cfg(any(flash_wl, flash_wb, flash_l4))]
164 unsafe {
165 pac::FLASH.cr().modify(|w| w.set_per(false));
166 }
167
168 #[cfg(any(flash_l0, flash_l1))]
169 unsafe {
170 pac::FLASH.pecr().modify(|w| {
171 w.set_erase(false);
172 w.set_prog(false);
173 });
174 }
175
176 self.clear_all_err();
177 if ret.is_err() {
178 return ret;
179 }
180 }
181
182 Ok(())
183 }
184
185 fn blocking_wait_ready(&self) -> Result<(), Error> {
186 loop {
187 let sr = unsafe { pac::FLASH.sr().read() };
188
189 if !sr.bsy() {
190 #[cfg(any(flash_wl, flash_wb, flash_l4))]
191 if sr.progerr() {
192 return Err(Error::Prog);
193 }
194
195 if sr.wrperr() {
196 return Err(Error::Protected);
197 }
198
199 if sr.pgaerr() {
200 return Err(Error::Unaligned);
201 }
202
203 if sr.sizerr() {
204 return Err(Error::Size);
205 }
206
207 #[cfg(any(flash_wl, flash_wb, flash_l4))]
208 if sr.miserr() {
209 return Err(Error::Miss);
210 }
211
212 #[cfg(any(flash_wl, flash_wb, flash_l4))]
213 if sr.pgserr() {
214 return Err(Error::Seq);
215 }
216 return Ok(());
217 }
218 }
219 } 87 }
220 88
221 fn clear_all_err(&mut self) { 89 fn clear_all_err(&mut self) {
222 unsafe { 90 unsafe { family::clear_all_err() };
223 pac::FLASH.sr().modify(|w| {
224 #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l0))]
225 if w.rderr() {
226 w.set_rderr(false);
227 }
228 #[cfg(any(flash_wl, flash_wb, flash_l4))]
229 if w.fasterr() {
230 w.set_fasterr(false);
231 }
232 #[cfg(any(flash_wl, flash_wb, flash_l4))]
233 if w.miserr() {
234 w.set_miserr(false);
235 }
236 #[cfg(any(flash_wl, flash_wb, flash_l4))]
237 if w.pgserr() {
238 w.set_pgserr(false);
239 }
240 if w.sizerr() {
241 w.set_sizerr(false);
242 }
243 if w.pgaerr() {
244 w.set_pgaerr(false);
245 }
246 if w.wrperr() {
247 w.set_wrperr(false);
248 }
249 #[cfg(any(flash_wl, flash_wb, flash_l4))]
250 if w.progerr() {
251 w.set_progerr(false);
252 }
253 #[cfg(any(flash_wl, flash_wb, flash_l4))]
254 if w.operr() {
255 w.set_operr(false);
256 }
257 });
258 }
259 } 91 }
260} 92}
261 93
@@ -274,6 +106,7 @@ pub enum Error {
274 Seq, 106 Seq,
275 Protected, 107 Protected,
276 Unaligned, 108 Unaligned,
109 Parallelism,
277} 110}
278 111
279impl<'d> ErrorType for Flash<'d> { 112impl<'d> ErrorType for Flash<'d> {
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index ba426128f..fbd2008c8 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -39,7 +39,7 @@ pub mod can;
39pub mod dac; 39pub mod dac;
40#[cfg(dcmi)] 40#[cfg(dcmi)]
41pub mod dcmi; 41pub mod dcmi;
42#[cfg(all(eth, feature = "net"))] 42#[cfg(eth)]
43pub mod eth; 43pub mod eth;
44#[cfg(feature = "exti")] 44#[cfg(feature = "exti")]
45pub mod exti; 45pub mod exti;
@@ -50,7 +50,9 @@ pub mod i2c;
50 50
51#[cfg(crc)] 51#[cfg(crc)]
52pub mod crc; 52pub mod crc;
53#[cfg(any(flash_l0, flash_l1, flash_wl, flash_wb, flash_l4))] 53#[cfg(any(
54 flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f7, flash_h7
55))]
54pub mod flash; 56pub mod flash;
55pub mod pwm; 57pub mod pwm;
56#[cfg(rng)] 58#[cfg(rng)]
@@ -61,7 +63,7 @@ pub mod sdmmc;
61pub mod spi; 63pub mod spi;
62#[cfg(usart)] 64#[cfg(usart)]
63pub mod usart; 65pub mod usart;
64#[cfg(feature = "usb-otg")] 66#[cfg(any(otgfs, otghs))]
65pub mod usb_otg; 67pub mod usb_otg;
66 68
67#[cfg(feature = "subghz")] 69#[cfg(feature = "subghz")]
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
new file mode 100644
index 000000000..b2fcb504e
--- /dev/null
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -0,0 +1,238 @@
1use atomic_polyfill::{compiler_fence, Ordering};
2use core::future::Future;
3use core::task::Poll;
4use embassy::waitqueue::WakerRegistration;
5use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
6use embassy_hal_common::ring_buffer::RingBuffer;
7use futures::future::poll_fn;
8
9use super::*;
10
11pub struct State<'d, T: Instance>(StateStorage<StateInner<'d, T>>);
12impl<'d, T: Instance> State<'d, T> {
13 pub fn new() -> Self {
14 Self(StateStorage::new())
15 }
16}
17
18struct StateInner<'d, T: Instance> {
19 phantom: PhantomData<&'d mut T>,
20
21 rx_waker: WakerRegistration,
22 rx: RingBuffer<'d>,
23
24 tx_waker: WakerRegistration,
25 tx: RingBuffer<'d>,
26}
27
28unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {}
29unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {}
30
31pub struct BufferedUart<'d, T: Instance> {
32 inner: PeripheralMutex<'d, StateInner<'d, T>>,
33}
34
35impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {}
36
37impl<'d, T: Instance> BufferedUart<'d, T> {
38 pub unsafe fn new(
39 state: &'d mut State<'d, T>,
40 _uart: Uart<'d, T, NoDma, NoDma>,
41 irq: impl Unborrow<Target = T::Interrupt> + 'd,
42 tx_buffer: &'d mut [u8],
43 rx_buffer: &'d mut [u8],
44 ) -> BufferedUart<'d, T> {
45 unborrow!(irq);
46
47 let r = T::regs();
48 r.cr1().modify(|w| {
49 w.set_rxneie(true);
50 w.set_idleie(true);
51 });
52
53 Self {
54 inner: PeripheralMutex::new_unchecked(irq, &mut state.0, move || StateInner {
55 phantom: PhantomData,
56 tx: RingBuffer::new(tx_buffer),
57 tx_waker: WakerRegistration::new(),
58
59 rx: RingBuffer::new(rx_buffer),
60 rx_waker: WakerRegistration::new(),
61 }),
62 }
63 }
64}
65
66impl<'d, T: Instance> StateInner<'d, T>
67where
68 Self: 'd,
69{
70 fn on_rx(&mut self) {
71 let r = T::regs();
72 unsafe {
73 let sr = sr(r).read();
74 clear_interrupt_flags(r, sr);
75
76 // This read also clears the error and idle interrupt flags on v1.
77 let b = rdr(r).read_volatile();
78
79 if sr.rxne() {
80 if sr.pe() {
81 warn!("Parity error");
82 }
83 if sr.fe() {
84 warn!("Framing error");
85 }
86 if sr.ne() {
87 warn!("Noise error");
88 }
89 if sr.ore() {
90 warn!("Overrun error");
91 }
92
93 let buf = self.rx.push_buf();
94 if !buf.is_empty() {
95 buf[0] = b;
96 self.rx.push(1);
97 } else {
98 warn!("RX buffer full, discard received byte");
99 }
100
101 if self.rx.is_full() {
102 self.rx_waker.wake();
103 }
104 }
105
106 if sr.idle() {
107 self.rx_waker.wake();
108 };
109 }
110 }
111
112 fn on_tx(&mut self) {
113 let r = T::regs();
114 unsafe {
115 if sr(r).read().txe() {
116 let buf = self.tx.pop_buf();
117 if !buf.is_empty() {
118 r.cr1().modify(|w| {
119 w.set_txeie(true);
120 });
121 tdr(r).write_volatile(buf[0].into());
122 self.tx.pop(1);
123 self.tx_waker.wake();
124 } else {
125 // Disable interrupt until we have something to transmit again
126 r.cr1().modify(|w| {
127 w.set_txeie(false);
128 });
129 }
130 }
131 }
132 }
133}
134
135impl<'d, T: Instance> PeripheralState for StateInner<'d, T>
136where
137 Self: 'd,
138{
139 type Interrupt = T::Interrupt;
140 fn on_interrupt(&mut self) {
141 self.on_rx();
142 self.on_tx();
143 }
144}
145
146impl embedded_io::Error for Error {
147 fn kind(&self) -> embedded_io::ErrorKind {
148 embedded_io::ErrorKind::Other
149 }
150}
151
152impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> {
153 type Error = Error;
154}
155
156impl<'d, T: Instance> embedded_io::asynch::Read for BufferedUart<'d, T> {
157 type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
158 where
159 Self: 'a;
160
161 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
162 poll_fn(move |cx| {
163 let mut do_pend = false;
164 let res = self.inner.with(|state| {
165 compiler_fence(Ordering::SeqCst);
166
167 // We have data ready in buffer? Return it.
168 let data = state.rx.pop_buf();
169 if !data.is_empty() {
170 let len = data.len().min(buf.len());
171 buf[..len].copy_from_slice(&data[..len]);
172
173 if state.rx.is_full() {
174 do_pend = true;
175 }
176 state.rx.pop(len);
177
178 return Poll::Ready(Ok(len));
179 }
180
181 state.rx_waker.register(cx.waker());
182 Poll::Pending
183 });
184
185 if do_pend {
186 self.inner.pend();
187 }
188
189 res
190 })
191 }
192}
193
194impl<'d, T: Instance> embedded_io::asynch::Write for BufferedUart<'d, T> {
195 type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
196 where
197 Self: 'a;
198
199 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
200 poll_fn(move |cx| {
201 let (poll, empty) = self.inner.with(|state| {
202 let empty = state.tx.is_empty();
203 let tx_buf = state.tx.push_buf();
204 if tx_buf.is_empty() {
205 state.tx_waker.register(cx.waker());
206 return (Poll::Pending, empty);
207 }
208
209 let n = core::cmp::min(tx_buf.len(), buf.len());
210 tx_buf[..n].copy_from_slice(&buf[..n]);
211 state.tx.push(n);
212
213 (Poll::Ready(Ok(n)), empty)
214 });
215 if empty {
216 self.inner.pend();
217 }
218 poll
219 })
220 }
221
222 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
223 where
224 Self: 'a;
225
226 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
227 poll_fn(move |cx| {
228 self.inner.with(|state| {
229 if !state.tx.is_empty() {
230 state.tx_waker.register(cx.waker());
231 return Poll::Pending;
232 }
233
234 Poll::Ready(Ok(()))
235 })
236 })
237 }
238}
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 22224215c..6feecd184 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -424,227 +424,10 @@ cfg_if::cfg_if! {
424 } 424 }
425} 425}
426 426
427#[cfg(feature = "nightly")]
427pub use buffered::*; 428pub use buffered::*;
428mod buffered { 429#[cfg(feature = "nightly")]
429 use atomic_polyfill::{compiler_fence, Ordering}; 430mod buffered;
430 use core::pin::Pin;
431 use core::task::Context;
432 use core::task::Poll;
433 use embassy::waitqueue::WakerRegistration;
434 use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
435 use embassy_hal_common::ring_buffer::RingBuffer;
436
437 use super::*;
438
439 pub struct State<'d, T: Instance>(StateStorage<StateInner<'d, T>>);
440 impl<'d, T: Instance> State<'d, T> {
441 pub fn new() -> Self {
442 Self(StateStorage::new())
443 }
444 }
445
446 struct StateInner<'d, T: Instance> {
447 phantom: PhantomData<&'d mut T>,
448
449 rx_waker: WakerRegistration,
450 rx: RingBuffer<'d>,
451
452 tx_waker: WakerRegistration,
453 tx: RingBuffer<'d>,
454 }
455
456 unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {}
457 unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {}
458
459 pub struct BufferedUart<'d, T: Instance> {
460 inner: PeripheralMutex<'d, StateInner<'d, T>>,
461 }
462
463 impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {}
464
465 impl<'d, T: Instance> BufferedUart<'d, T> {
466 pub unsafe fn new(
467 state: &'d mut State<'d, T>,
468 _uart: Uart<'d, T, NoDma, NoDma>,
469 irq: impl Unborrow<Target = T::Interrupt> + 'd,
470 tx_buffer: &'d mut [u8],
471 rx_buffer: &'d mut [u8],
472 ) -> BufferedUart<'d, T> {
473 unborrow!(irq);
474
475 let r = T::regs();
476 r.cr1().modify(|w| {
477 w.set_rxneie(true);
478 w.set_idleie(true);
479 });
480
481 Self {
482 inner: PeripheralMutex::new_unchecked(irq, &mut state.0, move || StateInner {
483 phantom: PhantomData,
484 tx: RingBuffer::new(tx_buffer),
485 tx_waker: WakerRegistration::new(),
486
487 rx: RingBuffer::new(rx_buffer),
488 rx_waker: WakerRegistration::new(),
489 }),
490 }
491 }
492 }
493
494 impl<'d, T: Instance> StateInner<'d, T>
495 where
496 Self: 'd,
497 {
498 fn on_rx(&mut self) {
499 let r = T::regs();
500 unsafe {
501 let sr = sr(r).read();
502 clear_interrupt_flags(r, sr);
503
504 // This read also clears the error and idle interrupt flags on v1.
505 let b = rdr(r).read_volatile();
506
507 if sr.rxne() {
508 if sr.pe() {
509 warn!("Parity error");
510 }
511 if sr.fe() {
512 warn!("Framing error");
513 }
514 if sr.ne() {
515 warn!("Noise error");
516 }
517 if sr.ore() {
518 warn!("Overrun error");
519 }
520
521 let buf = self.rx.push_buf();
522 if !buf.is_empty() {
523 buf[0] = b;
524 self.rx.push(1);
525 } else {
526 warn!("RX buffer full, discard received byte");
527 }
528
529 if self.rx.is_full() {
530 self.rx_waker.wake();
531 }
532 }
533
534 if sr.idle() {
535 self.rx_waker.wake();
536 };
537 }
538 }
539
540 fn on_tx(&mut self) {
541 let r = T::regs();
542 unsafe {
543 if sr(r).read().txe() {
544 let buf = self.tx.pop_buf();
545 if !buf.is_empty() {
546 r.cr1().modify(|w| {
547 w.set_txeie(true);
548 });
549 tdr(r).write_volatile(buf[0].into());
550 self.tx.pop(1);
551 self.tx_waker.wake();
552 } else {
553 // Disable interrupt until we have something to transmit again
554 r.cr1().modify(|w| {
555 w.set_txeie(false);
556 });
557 }
558 }
559 }
560 }
561 }
562
563 impl<'d, T: Instance> PeripheralState for StateInner<'d, T>
564 where
565 Self: 'd,
566 {
567 type Interrupt = T::Interrupt;
568 fn on_interrupt(&mut self) {
569 self.on_rx();
570 self.on_tx();
571 }
572 }
573
574 impl<'d, T: Instance> embassy::io::AsyncBufRead for BufferedUart<'d, T> {
575 fn poll_fill_buf(
576 mut self: Pin<&mut Self>,
577 cx: &mut Context<'_>,
578 ) -> Poll<Result<&[u8], embassy::io::Error>> {
579 self.inner.with(|state| {
580 compiler_fence(Ordering::SeqCst);
581
582 // We have data ready in buffer? Return it.
583 let buf = state.rx.pop_buf();
584 if !buf.is_empty() {
585 let buf: &[u8] = buf;
586 // Safety: buffer lives as long as uart
587 let buf: &[u8] = unsafe { core::mem::transmute(buf) };
588 return Poll::Ready(Ok(buf));
589 }
590
591 state.rx_waker.register(cx.waker());
592 Poll::<Result<&[u8], embassy::io::Error>>::Pending
593 })
594 }
595 fn consume(mut self: Pin<&mut Self>, amt: usize) {
596 let signal = self.inner.with(|state| {
597 let full = state.rx.is_full();
598 state.rx.pop(amt);
599 full
600 });
601 if signal {
602 self.inner.pend();
603 }
604 }
605 }
606
607 impl<'d, T: Instance> embassy::io::AsyncWrite for BufferedUart<'d, T> {
608 fn poll_write(
609 mut self: Pin<&mut Self>,
610 cx: &mut Context<'_>,
611 buf: &[u8],
612 ) -> Poll<Result<usize, embassy::io::Error>> {
613 let (poll, empty) = self.inner.with(|state| {
614 let empty = state.tx.is_empty();
615 let tx_buf = state.tx.push_buf();
616 if tx_buf.is_empty() {
617 state.tx_waker.register(cx.waker());
618 return (Poll::Pending, empty);
619 }
620
621 let n = core::cmp::min(tx_buf.len(), buf.len());
622 tx_buf[..n].copy_from_slice(&buf[..n]);
623 state.tx.push(n);
624
625 (Poll::Ready(Ok(n)), empty)
626 });
627 if empty {
628 self.inner.pend();
629 }
630 poll
631 }
632
633 fn poll_flush(
634 mut self: Pin<&mut Self>,
635 cx: &mut Context<'_>,
636 ) -> Poll<Result<(), embassy::io::Error>> {
637 self.inner.with(|state| {
638 if !state.tx.is_empty() {
639 state.tx_waker.register(cx.waker());
640 return Poll::Pending;
641 }
642
643 Poll::Ready(Ok(()))
644 })
645 }
646 }
647}
648 431
649#[cfg(usart_v1)] 432#[cfg(usart_v1)]
650fn tdr(r: crate::pac::usart::Usart) -> *mut u8 { 433fn tdr(r: crate::pac::usart::Usart) -> *mut u8 {
@@ -662,6 +445,7 @@ fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Sr, crate::p
662} 445}
663 446
664#[cfg(usart_v1)] 447#[cfg(usart_v1)]
448#[allow(unused)]
665unsafe fn clear_interrupt_flags(_r: crate::pac::usart::Usart, _sr: regs::Sr) { 449unsafe fn clear_interrupt_flags(_r: crate::pac::usart::Usart, _sr: regs::Sr) {
666 // On v1 the flags are cleared implicitly by reads and writes to DR. 450 // On v1 the flags are cleared implicitly by reads and writes to DR.
667} 451}
@@ -682,6 +466,7 @@ fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Ixr, crate::
682} 466}
683 467
684#[cfg(usart_v2)] 468#[cfg(usart_v2)]
469#[allow(unused)]
685unsafe fn clear_interrupt_flags(r: crate::pac::usart::Usart, sr: regs::Ixr) { 470unsafe fn clear_interrupt_flags(r: crate::pac::usart::Usart, sr: regs::Ixr) {
686 r.icr().write(|w| *w = sr); 471 r.icr().write(|w| *w = sr);
687} 472}
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/embassy/src/io/error.rs b/embassy/src/io/error.rs
deleted file mode 100644
index 2092c0d18..000000000
--- a/embassy/src/io/error.rs
+++ /dev/null
@@ -1,142 +0,0 @@
1/// Categories of errors that can occur.
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3#[cfg_attr(feature = "defmt", derive(defmt::Format))]
4#[non_exhaustive]
5pub enum Error {
6 /// An entity was not found, often a file.
7 NotFound,
8 /// The operation lacked the necessary privileges to complete.
9 PermissionDenied,
10 /// The connection was refused by the remote server.
11 ConnectionRefused,
12 /// The connection was reset by the remote server.
13 ConnectionReset,
14 /// The connection was aborted (terminated) by the remote server.
15 ConnectionAborted,
16 /// The network operation failed because it was not connected yet.
17 NotConnected,
18 /// A socket address could not be bound because the address is already in
19 /// use elsewhere.
20 AddrInUse,
21 /// A nonexistent interface was requested or the requested address was not
22 /// local.
23 AddrNotAvailable,
24 /// The operation failed because a pipe was closed.
25 BrokenPipe,
26 /// An entity already exists, often a file.
27 AlreadyExists,
28 /// The operation needs to block to complete, but the blocking operation was
29 /// requested to not occur.
30 WouldBlock,
31 /// A parameter was incorrect.
32 InvalidInput,
33 /// Data not valid for the operation were encountered.
34 ///
35 /// Unlike [`InvalidInput`], this typically means that the operation
36 /// parameters were valid, however the error was caused by malformed
37 /// input data.
38 ///
39 /// For example, a function that reads a file into a string will error with
40 /// `InvalidData` if the file's contents are not valid UTF-8.
41 ///
42 /// [`InvalidInput`]: #variant.InvalidInput
43 InvalidData,
44 /// The I/O operation's timeout expired, causing it to be canceled.
45 TimedOut,
46 /// An error returned when an operation could not be completed because a
47 /// call to [`write`] returned [`Ok(0)`].
48 ///
49 /// This typically means that an operation could only succeed if it wrote a
50 /// particular number of bytes but only a smaller number of bytes could be
51 /// written.
52 ///
53 /// [`write`]: ../../std/io/trait.Write.html#tymethod.write
54 /// [`Ok(0)`]: ../../std/io/type.Result.html
55 WriteZero,
56 /// This operation was interrupted.
57 ///
58 /// Interrupted operations can typically be retried.
59 Interrupted,
60
61 /// An error returned when an operation could not be completed because an
62 /// "end of file" was reached prematurely.
63 ///
64 /// This typically means that an operation could only succeed if it read a
65 /// particular number of bytes but only a smaller number of bytes could be
66 /// read.
67 UnexpectedEof,
68
69 /// An operation would have read more data if the given buffer was large.
70 ///
71 /// This typically means that the buffer has been filled with the first N bytes
72 /// of the read data.
73 Truncated,
74
75 /// Any I/O error not part of this list.
76 Other,
77}
78
79pub type Result<T> = core::result::Result<T, Error>;
80
81#[cfg(feature = "std")]
82impl From<std::io::Error> for Error {
83 fn from(err: std::io::Error) -> Error {
84 match err.kind() {
85 std::io::ErrorKind::NotFound => Error::NotFound,
86 std::io::ErrorKind::PermissionDenied => Error::PermissionDenied,
87 std::io::ErrorKind::ConnectionRefused => Error::ConnectionRefused,
88 std::io::ErrorKind::ConnectionReset => Error::ConnectionReset,
89 std::io::ErrorKind::ConnectionAborted => Error::ConnectionAborted,
90 std::io::ErrorKind::NotConnected => Error::NotConnected,
91 std::io::ErrorKind::AddrInUse => Error::AddrInUse,
92 std::io::ErrorKind::AddrNotAvailable => Error::AddrNotAvailable,
93 std::io::ErrorKind::BrokenPipe => Error::BrokenPipe,
94 std::io::ErrorKind::AlreadyExists => Error::AlreadyExists,
95 std::io::ErrorKind::WouldBlock => Error::WouldBlock,
96 std::io::ErrorKind::InvalidInput => Error::InvalidInput,
97 std::io::ErrorKind::InvalidData => Error::InvalidData,
98 std::io::ErrorKind::TimedOut => Error::TimedOut,
99 std::io::ErrorKind::WriteZero => Error::WriteZero,
100 std::io::ErrorKind::Interrupted => Error::Interrupted,
101 std::io::ErrorKind::UnexpectedEof => Error::UnexpectedEof,
102 _ => Error::Other,
103 }
104 }
105}
106
107#[cfg(feature = "std")]
108impl From<Error> for std::io::Error {
109 fn from(e: Error) -> Self {
110 let kind = match e {
111 Error::NotFound => std::io::ErrorKind::NotFound,
112 Error::PermissionDenied => std::io::ErrorKind::PermissionDenied,
113 Error::ConnectionRefused => std::io::ErrorKind::ConnectionRefused,
114 Error::ConnectionReset => std::io::ErrorKind::ConnectionReset,
115 Error::ConnectionAborted => std::io::ErrorKind::ConnectionAborted,
116 Error::NotConnected => std::io::ErrorKind::NotConnected,
117 Error::AddrInUse => std::io::ErrorKind::AddrInUse,
118 Error::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable,
119 Error::BrokenPipe => std::io::ErrorKind::BrokenPipe,
120 Error::AlreadyExists => std::io::ErrorKind::AlreadyExists,
121 Error::WouldBlock => std::io::ErrorKind::WouldBlock,
122 Error::InvalidInput => std::io::ErrorKind::InvalidInput,
123 Error::InvalidData => std::io::ErrorKind::InvalidData,
124 Error::TimedOut => std::io::ErrorKind::TimedOut,
125 Error::WriteZero => std::io::ErrorKind::WriteZero,
126 Error::Interrupted => std::io::ErrorKind::Interrupted,
127 Error::UnexpectedEof => std::io::ErrorKind::UnexpectedEof,
128 Error::Truncated => std::io::ErrorKind::Other,
129 Error::Other => std::io::ErrorKind::Other,
130 };
131 std::io::Error::new(kind, "embassy::io::Error")
132 }
133}
134
135impl core::fmt::Display for Error {
136 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
137 write!(f, "{:?}", self)
138 }
139}
140
141#[cfg(feature = "std")]
142impl std::error::Error for Error {}
diff --git a/embassy/src/io/mod.rs b/embassy/src/io/mod.rs
deleted file mode 100644
index 52b050971..000000000
--- a/embassy/src/io/mod.rs
+++ /dev/null
@@ -1,11 +0,0 @@
1mod error;
2#[cfg(feature = "std")]
3mod std;
4mod traits;
5mod util;
6
7pub use self::error::*;
8#[cfg(feature = "std")]
9pub use self::std::*;
10pub use self::traits::*;
11pub use self::util::*;
diff --git a/embassy/src/io/std.rs b/embassy/src/io/std.rs
deleted file mode 100644
index 580d52891..000000000
--- a/embassy/src/io/std.rs
+++ /dev/null
@@ -1,41 +0,0 @@
1use core::pin::Pin;
2use core::task::{Context, Poll};
3use futures::io as std_io;
4
5use super::{AsyncBufRead, AsyncWrite, Result};
6
7pub struct FromStdIo<T>(T);
8
9impl<T> FromStdIo<T> {
10 pub fn new(inner: T) -> Self {
11 Self(inner)
12 }
13}
14
15impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> {
16 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
17 let Self(inner) = unsafe { self.get_unchecked_mut() };
18 unsafe { Pin::new_unchecked(inner) }
19 .poll_fill_buf(cx)
20 .map_err(|e| e.into())
21 }
22 fn consume(self: Pin<&mut Self>, amt: usize) {
23 let Self(inner) = unsafe { self.get_unchecked_mut() };
24 unsafe { Pin::new_unchecked(inner) }.consume(amt)
25 }
26}
27
28impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> {
29 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
30 let Self(inner) = unsafe { self.get_unchecked_mut() };
31 unsafe { Pin::new_unchecked(inner) }
32 .poll_write(cx, buf)
33 .map_err(|e| e.into())
34 }
35 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
36 let Self(inner) = unsafe { self.get_unchecked_mut() };
37 unsafe { Pin::new_unchecked(inner) }
38 .poll_flush(cx)
39 .map_err(|e| e.into())
40 }
41}
diff --git a/embassy/src/io/traits.rs b/embassy/src/io/traits.rs
deleted file mode 100644
index 06500a687..000000000
--- a/embassy/src/io/traits.rs
+++ /dev/null
@@ -1,175 +0,0 @@
1use core::ops::DerefMut;
2use core::pin::Pin;
3use core::task::{Context, Poll};
4
5#[cfg(feature = "alloc")]
6use alloc::boxed::Box;
7
8use super::error::Result;
9
10/// Read bytes asynchronously.
11///
12/// This trait is analogous to the `std::io::BufRead` trait, but integrates
13/// with the asynchronous task system. In particular, the `poll_fill_buf`
14/// method, unlike `BufRead::fill_buf`, will automatically queue the current task
15/// for wakeup and return if data is not yet available, rather than blocking
16/// the calling thread.
17pub trait AsyncBufRead {
18 /// Attempt to return the contents of the internal buffer, filling it with more data
19 /// from the inner reader if it is empty.
20 ///
21 /// On success, returns `Poll::Ready(Ok(buf))`.
22 ///
23 /// If no data is available for reading, the method returns
24 /// `Poll::Pending` and arranges for the current task (via
25 /// `cx.waker().wake_by_ref()`) to receive a notification when the object becomes
26 /// readable or is closed.
27 ///
28 /// This function is a lower-level call. It needs to be paired with the
29 /// [`consume`] method to function properly. When calling this
30 /// method, none of the contents will be "read" in the sense that later
31 /// calling [`poll_fill_buf`] may return the same contents. As such, [`consume`] must
32 /// be called with the number of bytes that are consumed from this buffer to
33 /// ensure that the bytes are never returned twice.
34 ///
35 /// [`poll_fill_buf`]: AsyncBufRead::poll_fill_buf
36 /// [`consume`]: AsyncBufRead::consume
37 ///
38 /// An empty buffer returned indicates that the stream has reached EOF.
39 ///
40 /// # Implementation
41 ///
42 /// This function may not return errors of kind `WouldBlock` or
43 /// `Interrupted`. Implementations must convert `WouldBlock` into
44 /// `Poll::Pending` and either internally retry or convert
45 /// `Interrupted` into another error kind.
46 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>>;
47
48 /// Tells this buffer that `amt` bytes have been consumed from the buffer,
49 /// so they should no longer be returned in calls to [`poll_fill_buf`].
50 ///
51 /// This function is a lower-level call. It needs to be paired with the
52 /// [`poll_fill_buf`] method to function properly. This function does
53 /// not perform any I/O, it simply informs this object that some amount of
54 /// its buffer, returned from [`poll_fill_buf`], has been consumed and should
55 /// no longer be returned. As such, this function may do odd things if
56 /// [`poll_fill_buf`] isn't called before calling it.
57 ///
58 /// The `amt` must be `<=` the number of bytes in the buffer returned by
59 /// [`poll_fill_buf`].
60 ///
61 /// [`poll_fill_buf`]: AsyncBufRead::poll_fill_buf
62 fn consume(self: Pin<&mut Self>, amt: usize);
63}
64
65/// Write bytes asynchronously.
66///
67/// This trait is analogous to the `core::io::Write` trait, but integrates
68/// with the asynchronous task system. In particular, the `poll_write`
69/// method, unlike `Write::write`, will automatically queue the current task
70/// for wakeup and return if the writer cannot take more data, rather than blocking
71/// the calling thread.
72pub trait AsyncWrite {
73 /// Attempt to write bytes from `buf` into the object.
74 ///
75 /// On success, returns `Poll::Ready(Ok(num_bytes_written))`.
76 ///
77 /// If the object is not ready for writing, the method returns
78 /// `Poll::Pending` and arranges for the current task (via
79 /// `cx.waker().wake_by_ref()`) to receive a notification when the object becomes
80 /// writable or is closed.
81 ///
82 /// # Implementation
83 ///
84 /// This function may not return errors of kind `WouldBlock` or
85 /// `Interrupted`. Implementations must convert `WouldBlock` into
86 /// `Poll::Pending` and either internally retry or convert
87 /// `Interrupted` into another error kind.
88 ///
89 /// `poll_write` must try to make progress by flushing the underlying object if
90 /// that is the only way the underlying object can become writable again.
91 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>;
92
93 /// Attempt to flush the object, ensuring that any buffered data reach their destination.
94 ///
95 /// On success, returns Poll::Ready(Ok(())).
96 ///
97 /// If flushing cannot immediately complete, this method returns [Poll::Pending] and arranges for the
98 /// current task (via cx.waker()) to receive a notification when the object can make progress
99 /// towards flushing.
100 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>>;
101}
102
103macro_rules! defer_async_read {
104 () => {
105 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
106 Pin::new(&mut **self.get_mut()).poll_fill_buf(cx)
107 }
108
109 fn consume(mut self: Pin<&mut Self>, amt: usize) {
110 Pin::new(&mut **self).consume(amt)
111 }
112 };
113}
114
115#[cfg(feature = "alloc")]
116impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for Box<T> {
117 defer_async_read!();
118}
119
120impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for &mut T {
121 defer_async_read!();
122}
123
124impl<P> AsyncBufRead for Pin<P>
125where
126 P: DerefMut + Unpin,
127 P::Target: AsyncBufRead,
128{
129 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
130 self.get_mut().as_mut().poll_fill_buf(cx)
131 }
132
133 fn consume(self: Pin<&mut Self>, amt: usize) {
134 self.get_mut().as_mut().consume(amt)
135 }
136}
137
138macro_rules! deref_async_write {
139 () => {
140 fn poll_write(
141 mut self: Pin<&mut Self>,
142 cx: &mut Context<'_>,
143 buf: &[u8],
144 ) -> Poll<Result<usize>> {
145 Pin::new(&mut **self).poll_write(cx, buf)
146 }
147
148 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
149 Pin::new(&mut **self).poll_flush(cx)
150 }
151 };
152}
153
154#[cfg(feature = "alloc")]
155impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> {
156 deref_async_write!();
157}
158
159impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for &mut T {
160 deref_async_write!();
161}
162
163impl<P> AsyncWrite for Pin<P>
164where
165 P: DerefMut + Unpin,
166 P::Target: AsyncWrite,
167{
168 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
169 self.get_mut().as_mut().poll_write(cx, buf)
170 }
171
172 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
173 self.get_mut().as_mut().poll_flush(cx)
174 }
175}
diff --git a/embassy/src/io/util/copy_buf.rs b/embassy/src/io/util/copy_buf.rs
deleted file mode 100644
index 6d7932a0f..000000000
--- a/embassy/src/io/util/copy_buf.rs
+++ /dev/null
@@ -1,80 +0,0 @@
1use core::future::Future;
2use core::pin::Pin;
3use core::task::{Context, Poll};
4use futures::ready;
5use pin_project::pin_project;
6
7use crate::io::{AsyncBufRead, AsyncWrite, Error, Result};
8
9/// Creates a future which copies all the bytes from one object to another.
10///
11/// The returned future will copy all the bytes read from this `AsyncBufRead` into the
12/// `writer` specified. This future will only complete once the `reader` has hit
13/// EOF and all bytes have been written to and flushed from the `writer`
14/// provided.
15///
16/// On success the number of bytes is returned.
17///
18/// # Examples
19///
20/// ``` ignore
21/// # futures::executor::block_on(async {
22/// use futures::io::{self, AsyncWriteExt, Cursor};
23///
24/// let reader = Cursor::new([1, 2, 3, 4]);
25/// let mut writer = Cursor::new(vec![0u8; 5]);
26///
27/// let bytes = io::copy_buf(reader, &mut writer).await?;
28/// writer.close().await?;
29///
30/// assert_eq!(bytes, 4);
31/// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]);
32/// # Ok::<(), Box<dyn std::error::Error>>(()) }).unwrap();
33/// ```
34pub fn copy_buf<R, W>(reader: R, writer: &mut W) -> CopyBuf<'_, R, W>
35where
36 R: AsyncBufRead,
37 W: AsyncWrite + Unpin + ?Sized,
38{
39 CopyBuf {
40 reader,
41 writer,
42 amt: 0,
43 }
44}
45
46/// Future for the [`copy_buf()`] function.
47#[pin_project]
48#[derive(Debug)]
49#[must_use = "futures do nothing unless you `.await` or poll them"]
50pub struct CopyBuf<'a, R, W: ?Sized> {
51 #[pin]
52 reader: R,
53 writer: &'a mut W,
54 amt: usize,
55}
56
57impl<R, W> Future for CopyBuf<'_, R, W>
58where
59 R: AsyncBufRead,
60 W: AsyncWrite + Unpin + ?Sized,
61{
62 type Output = Result<usize>;
63
64 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
65 let mut this = self.project();
66 loop {
67 let buffer = ready!(this.reader.as_mut().poll_fill_buf(cx))?;
68 if buffer.is_empty() {
69 return Poll::Ready(Ok(*this.amt));
70 }
71
72 let i = ready!(Pin::new(&mut this.writer).poll_write(cx, buffer))?;
73 if i == 0 {
74 return Poll::Ready(Err(Error::WriteZero));
75 }
76 *this.amt += i;
77 this.reader.as_mut().consume(i);
78 }
79 }
80}
diff --git a/embassy/src/io/util/drain.rs b/embassy/src/io/util/drain.rs
deleted file mode 100644
index 2542876d8..000000000
--- a/embassy/src/io/util/drain.rs
+++ /dev/null
@@ -1,41 +0,0 @@
1use core::pin::Pin;
2use futures::future::Future;
3use futures::task::{Context, Poll};
4
5use super::super::error::Result;
6use super::super::traits::AsyncBufRead;
7
8pub struct Drain<'a, R: ?Sized> {
9 reader: &'a mut R,
10}
11
12impl<R: ?Sized + Unpin> Unpin for Drain<'_, R> {}
13
14impl<'a, R: AsyncBufRead + ?Sized + Unpin> Drain<'a, R> {
15 pub(super) fn new(reader: &'a mut R) -> Self {
16 Self { reader }
17 }
18}
19
20impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for Drain<'a, R> {
21 type Output = Result<usize>;
22
23 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
24 let Self { reader } = &mut *self;
25 let mut reader = Pin::new(reader);
26
27 let mut n = 0;
28
29 loop {
30 match reader.as_mut().poll_fill_buf(cx) {
31 Poll::Pending => return Poll::Ready(Ok(n)),
32 Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
33 Poll::Ready(Ok(buf)) => {
34 let len = buf.len();
35 n += len;
36 reader.as_mut().consume(len);
37 }
38 }
39 }
40 }
41}
diff --git a/embassy/src/io/util/flush.rs b/embassy/src/io/util/flush.rs
deleted file mode 100644
index 966ef10fb..000000000
--- a/embassy/src/io/util/flush.rs
+++ /dev/null
@@ -1,32 +0,0 @@
1use core::pin::Pin;
2use futures::future::Future;
3use futures::ready;
4use futures::task::{Context, Poll};
5
6use super::super::error::Result;
7use super::super::traits::AsyncWrite;
8
9/// Future for the [`flush`](super::AsyncWriteExt::flush) method.
10#[derive(Debug)]
11#[must_use = "futures do nothing unless you `.await` or poll them"]
12pub struct Flush<'a, W: ?Sized> {
13 writer: &'a mut W,
14}
15
16impl<W: ?Sized + Unpin> Unpin for Flush<'_, W> {}
17
18impl<'a, W: AsyncWrite + ?Sized + Unpin> Flush<'a, W> {
19 pub(super) fn new(writer: &'a mut W) -> Self {
20 Flush { writer }
21 }
22}
23
24impl<W: AsyncWrite + ?Sized + Unpin> Future for Flush<'_, W> {
25 type Output = Result<()>;
26
27 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
28 let this = &mut *self;
29 let _ = ready!(Pin::new(&mut this.writer).poll_flush(cx))?;
30 Poll::Ready(Ok(()))
31 }
32}
diff --git a/embassy/src/io/util/mod.rs b/embassy/src/io/util/mod.rs
deleted file mode 100644
index 49758ba99..000000000
--- a/embassy/src/io/util/mod.rs
+++ /dev/null
@@ -1,177 +0,0 @@
1use core::cmp::min;
2use core::pin::Pin;
3use core::task::{Context, Poll};
4use futures::ready;
5
6mod read;
7pub use self::read::Read;
8
9mod read_buf;
10pub use self::read_buf::ReadBuf;
11
12mod read_byte;
13pub use self::read_byte::ReadByte;
14
15mod read_exact;
16pub use self::read_exact::ReadExact;
17
18mod read_while;
19pub use self::read_while::ReadWhile;
20
21mod read_to_end;
22pub use self::read_to_end::ReadToEnd;
23
24mod skip_while;
25pub use self::skip_while::SkipWhile;
26
27mod drain;
28pub use self::drain::Drain;
29
30mod flush;
31pub use self::flush::Flush;
32
33mod write;
34pub use self::write::Write;
35
36mod write_all;
37pub use self::write_all::WriteAll;
38
39mod write_byte;
40pub use self::write_byte::WriteByte;
41
42#[cfg(feature = "alloc")]
43mod split;
44#[cfg(feature = "alloc")]
45pub use self::split::{split, ReadHalf, WriteHalf};
46
47mod copy_buf;
48pub use self::copy_buf::{copy_buf, CopyBuf};
49
50use super::error::Result;
51use super::traits::{AsyncBufRead, AsyncWrite};
52
53pub trait AsyncBufReadExt: AsyncBufRead {
54 fn poll_read(
55 mut self: Pin<&mut Self>,
56 cx: &mut Context<'_>,
57 buf: &mut [u8],
58 ) -> Poll<Result<usize>>
59 where
60 Self: Unpin,
61 {
62 let mut this = &mut *self;
63 let rbuf = ready!(Pin::new(&mut this).poll_fill_buf(cx))?;
64 let n = min(buf.len(), rbuf.len());
65 buf[..n].copy_from_slice(&rbuf[..n]);
66 Pin::new(&mut this).consume(n);
67 Poll::Ready(Ok(n))
68 }
69
70 fn read_while<'a, F: Fn(u8) -> bool>(
71 &'a mut self,
72 buf: &'a mut [u8],
73 f: F,
74 ) -> ReadWhile<'a, Self, F>
75 where
76 Self: Unpin,
77 {
78 ReadWhile::new(self, f, buf)
79 }
80
81 fn skip_while<F: Fn(u8) -> bool>(&mut self, f: F) -> SkipWhile<Self, F>
82 where
83 Self: Unpin,
84 {
85 SkipWhile::new(self, f)
86 }
87
88 fn drain(&mut self) -> Drain<Self>
89 where
90 Self: Unpin,
91 {
92 Drain::new(self)
93 }
94
95 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self>
96 where
97 Self: Unpin,
98 {
99 Read::new(self, buf)
100 }
101
102 fn read_buf(&mut self) -> ReadBuf<Self>
103 where
104 Self: Unpin,
105 {
106 ReadBuf::new(self)
107 }
108
109 fn read_byte(&mut self) -> ReadByte<Self>
110 where
111 Self: Unpin,
112 {
113 ReadByte::new(self)
114 }
115
116 fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self>
117 where
118 Self: Unpin,
119 {
120 ReadExact::new(self, buf)
121 }
122
123 fn read_to_end<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadToEnd<'a, Self>
124 where
125 Self: Unpin,
126 {
127 ReadToEnd::new(self, buf)
128 }
129}
130
131impl<R: AsyncBufRead + ?Sized> AsyncBufReadExt for R {}
132
133pub async fn read_line<R: AsyncBufRead + Unpin + ?Sized>(
134 r: &mut R,
135 buf: &mut [u8],
136) -> Result<usize> {
137 r.skip_while(|b| b == b'\r' || b == b'\n').await?;
138 let n = r.read_while(buf, |b| b != b'\r' && b != b'\n').await?;
139 r.skip_while(|b| b == b'\r').await?;
140 //assert_eq!(b'\n', r.read_byte().await?);
141 r.read_byte().await?;
142 Ok(n)
143}
144
145pub trait AsyncWriteExt: AsyncWrite {
146 fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self>
147 where
148 Self: Unpin,
149 {
150 WriteAll::new(self, buf)
151 }
152
153 fn write_byte(&mut self, byte: u8) -> WriteByte<Self>
154 where
155 Self: Unpin,
156 {
157 WriteByte::new(self, byte)
158 }
159
160 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Write<'a, Self>
161 where
162 Self: Unpin,
163 {
164 Write::new(self, buf)
165 }
166
167 /// Awaits until all bytes have actually been written, and
168 /// not just enqueued as per the other "write" methods.
169 fn flush<'a>(&mut self) -> Flush<Self>
170 where
171 Self: Unpin,
172 {
173 Flush::new(self)
174 }
175}
176
177impl<R: AsyncWrite + ?Sized> AsyncWriteExt for R {}
diff --git a/embassy/src/io/util/read.rs b/embassy/src/io/util/read.rs
deleted file mode 100644
index 274b43a43..000000000
--- a/embassy/src/io/util/read.rs
+++ /dev/null
@@ -1,39 +0,0 @@
1use super::super::error::Result;
2use super::super::traits::AsyncBufRead;
3
4use core::cmp::min;
5
6use core::pin::Pin;
7use futures::future::Future;
8use futures::ready;
9use futures::task::{Context, Poll};
10
11/// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method.
12#[derive(Debug)]
13#[must_use = "futures do nothing unless you `.await` or poll them"]
14pub struct Read<'a, R: ?Sized> {
15 reader: &'a mut R,
16 buf: &'a mut [u8],
17}
18
19impl<R: ?Sized + Unpin> Unpin for Read<'_, R> {}
20
21impl<'a, R: AsyncBufRead + ?Sized + Unpin> Read<'a, R> {
22 pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
23 Read { reader, buf }
24 }
25}
26
27impl<R: AsyncBufRead + ?Sized + Unpin> Future for Read<'_, R> {
28 type Output = Result<usize>;
29
30 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
31 let this = &mut *self;
32 let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?;
33
34 let n = min(this.buf.len(), buf.len());
35 this.buf[..n].copy_from_slice(&buf[..n]);
36 Pin::new(&mut this.reader).consume(n);
37 Poll::Ready(Ok(n))
38 }
39}
diff --git a/embassy/src/io/util/read_buf.rs b/embassy/src/io/util/read_buf.rs
deleted file mode 100644
index 71299b05e..000000000
--- a/embassy/src/io/util/read_buf.rs
+++ /dev/null
@@ -1,34 +0,0 @@
1use super::super::error::Result;
2use super::super::traits::AsyncBufRead;
3
4use core::pin::Pin;
5use futures::future::Future;
6use futures::ready;
7use futures::task::{Context, Poll};
8
9pub struct ReadBuf<'a, R: ?Sized> {
10 reader: Option<&'a mut R>,
11}
12
13impl<R: ?Sized + Unpin> Unpin for ReadBuf<'_, R> {}
14
15impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadBuf<'a, R> {
16 pub(super) fn new(reader: &'a mut R) -> Self {
17 ReadBuf {
18 reader: Some(reader),
19 }
20 }
21}
22
23impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadBuf<'a, R> {
24 type Output = Result<&'a [u8]>;
25
26 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
27 let this = &mut *self;
28
29 let buf = ready!(Pin::new(this.reader.as_mut().unwrap()).poll_fill_buf(cx))?;
30 let buf: &'a [u8] = unsafe { core::mem::transmute(buf) };
31 this.reader = None;
32 Poll::Ready(Ok(buf))
33 }
34}
diff --git a/embassy/src/io/util/read_byte.rs b/embassy/src/io/util/read_byte.rs
deleted file mode 100644
index e5bbcb1ff..000000000
--- a/embassy/src/io/util/read_byte.rs
+++ /dev/null
@@ -1,36 +0,0 @@
1use core::pin::Pin;
2use futures::future::Future;
3use futures::ready;
4use futures::task::{Context, Poll};
5
6use super::super::error::{Error, Result};
7use super::super::traits::AsyncBufRead;
8
9pub struct ReadByte<'a, R: ?Sized> {
10 reader: &'a mut R,
11}
12
13impl<R: ?Sized + Unpin> Unpin for ReadByte<'_, R> {}
14
15impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadByte<'a, R> {
16 pub(super) fn new(reader: &'a mut R) -> Self {
17 Self { reader }
18 }
19}
20
21impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadByte<'a, R> {
22 type Output = Result<u8>;
23
24 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
25 let Self { reader } = &mut *self;
26 let mut reader = Pin::new(reader);
27 let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
28 if rbuf.is_empty() {
29 return Poll::Ready(Err(Error::UnexpectedEof));
30 }
31
32 let r = rbuf[0];
33 reader.as_mut().consume(1);
34 Poll::Ready(Ok(r))
35 }
36}
diff --git a/embassy/src/io/util/read_exact.rs b/embassy/src/io/util/read_exact.rs
deleted file mode 100644
index 4eecba4cc..000000000
--- a/embassy/src/io/util/read_exact.rs
+++ /dev/null
@@ -1,48 +0,0 @@
1use super::super::error::{Error, Result};
2use super::super::traits::AsyncBufRead;
3
4use core::cmp::min;
5use core::mem;
6use core::pin::Pin;
7use futures::future::Future;
8use futures::ready;
9use futures::task::{Context, Poll};
10
11/// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method.
12#[derive(Debug)]
13#[must_use = "futures do nothing unless you `.await` or poll them"]
14pub struct ReadExact<'a, R: ?Sized> {
15 reader: &'a mut R,
16 buf: &'a mut [u8],
17}
18
19impl<R: ?Sized + Unpin> Unpin for ReadExact<'_, R> {}
20
21impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadExact<'a, R> {
22 pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
23 ReadExact { reader, buf }
24 }
25}
26
27impl<R: AsyncBufRead + ?Sized + Unpin> Future for ReadExact<'_, R> {
28 type Output = Result<()>;
29
30 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
31 let this = &mut *self;
32 while !this.buf.is_empty() {
33 let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?;
34 if buf.is_empty() {
35 return Poll::Ready(Err(Error::UnexpectedEof));
36 }
37
38 let n = min(this.buf.len(), buf.len());
39 this.buf[..n].copy_from_slice(&buf[..n]);
40 Pin::new(&mut this.reader).consume(n);
41 {
42 let (_, rest) = mem::take(&mut this.buf).split_at_mut(n);
43 this.buf = rest;
44 }
45 }
46 Poll::Ready(Ok(()))
47 }
48}
diff --git a/embassy/src/io/util/read_to_end.rs b/embassy/src/io/util/read_to_end.rs
deleted file mode 100644
index d09999ad0..000000000
--- a/embassy/src/io/util/read_to_end.rs
+++ /dev/null
@@ -1,48 +0,0 @@
1use core::cmp::min;
2use core::pin::Pin;
3use futures::future::Future;
4use futures::ready;
5use futures::task::{Context, Poll};
6
7use super::super::error::{Error, Result};
8use super::super::traits::AsyncBufRead;
9
10pub struct ReadToEnd<'a, R: ?Sized> {
11 reader: &'a mut R,
12 buf: &'a mut [u8],
13 n: usize,
14}
15
16impl<R: ?Sized + Unpin> Unpin for ReadToEnd<'_, R> {}
17
18impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadToEnd<'a, R> {
19 pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
20 Self { reader, buf, n: 0 }
21 }
22}
23
24impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadToEnd<'a, R> {
25 type Output = Result<usize>;
26
27 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
28 let Self { reader, buf, n } = &mut *self;
29 let mut reader = Pin::new(reader);
30 loop {
31 let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
32 if rbuf.is_empty() {
33 return Poll::Ready(Ok(*n));
34 }
35
36 if *n == buf.len() {
37 return Poll::Ready(Err(Error::Truncated));
38 }
39
40 // truncate data if it doesn't fit in buf
41 let p = min(rbuf.len(), buf.len() - *n);
42 buf[*n..*n + p].copy_from_slice(&rbuf[..p]);
43 *n += p;
44
45 reader.as_mut().consume(p);
46 }
47 }
48}
diff --git a/embassy/src/io/util/read_while.rs b/embassy/src/io/util/read_while.rs
deleted file mode 100644
index e24638039..000000000
--- a/embassy/src/io/util/read_while.rs
+++ /dev/null
@@ -1,61 +0,0 @@
1use core::cmp::min;
2use core::pin::Pin;
3use futures::future::Future;
4use futures::ready;
5use futures::task::{Context, Poll};
6
7use super::super::error::{Error, Result};
8use super::super::traits::AsyncBufRead;
9
10pub struct ReadWhile<'a, R: ?Sized, F> {
11 reader: &'a mut R,
12 buf: &'a mut [u8],
13 n: usize,
14 f: F,
15}
16
17impl<R: ?Sized + Unpin, F> Unpin for ReadWhile<'_, R, F> {}
18
19impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> ReadWhile<'a, R, F> {
20 pub(super) fn new(reader: &'a mut R, f: F, buf: &'a mut [u8]) -> Self {
21 Self {
22 reader,
23 f,
24 buf,
25 n: 0,
26 }
27 }
28}
29
30impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for ReadWhile<'a, R, F> {
31 type Output = Result<usize>;
32
33 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
34 let Self { reader, f, buf, n } = &mut *self;
35 let mut reader = Pin::new(reader);
36 loop {
37 let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
38 if rbuf.is_empty() {
39 return Poll::Ready(Err(Error::UnexpectedEof));
40 }
41
42 let (p, done) = match rbuf.iter().position(|&b| !f(b)) {
43 Some(p) => (p, true),
44 None => (rbuf.len(), false),
45 };
46
47 // truncate data if it doesn't fit in buf
48 let p2 = min(p, buf.len() - *n);
49 buf[*n..*n + p2].copy_from_slice(&rbuf[..p2]);
50 *n += p2;
51
52 // consume it all, even if it doesn't fit.
53 // Otherwise we can deadlock because we never read to the ending char
54 reader.as_mut().consume(p);
55
56 if done {
57 return Poll::Ready(Ok(*n));
58 }
59 }
60 }
61}
diff --git a/embassy/src/io/util/skip_while.rs b/embassy/src/io/util/skip_while.rs
deleted file mode 100644
index 6f0e167df..000000000
--- a/embassy/src/io/util/skip_while.rs
+++ /dev/null
@@ -1,45 +0,0 @@
1use core::iter::Iterator;
2use core::pin::Pin;
3use futures::future::Future;
4use futures::ready;
5use futures::task::{Context, Poll};
6
7use super::super::error::{Error, Result};
8use super::super::traits::AsyncBufRead;
9
10pub struct SkipWhile<'a, R: ?Sized, F> {
11 reader: &'a mut R,
12 f: F,
13}
14
15impl<R: ?Sized + Unpin, F> Unpin for SkipWhile<'_, R, F> {}
16
17impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> SkipWhile<'a, R, F> {
18 pub(super) fn new(reader: &'a mut R, f: F) -> Self {
19 Self { reader, f }
20 }
21}
22
23impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for SkipWhile<'a, R, F> {
24 type Output = Result<()>;
25
26 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
27 let Self { reader, f } = &mut *self;
28 let mut reader = Pin::new(reader);
29 loop {
30 let buf = ready!(reader.as_mut().poll_fill_buf(cx))?;
31 if buf.is_empty() {
32 return Poll::Ready(Err(Error::UnexpectedEof));
33 }
34
35 let (p, done) = match buf.iter().position(|b| !f(*b)) {
36 Some(p) => (p, true),
37 None => (buf.len(), false),
38 };
39 reader.as_mut().consume(p);
40 if done {
41 return Poll::Ready(Ok(()));
42 }
43 }
44 }
45}
diff --git a/embassy/src/io/util/split.rs b/embassy/src/io/util/split.rs
deleted file mode 100644
index cc029aa53..000000000
--- a/embassy/src/io/util/split.rs
+++ /dev/null
@@ -1,43 +0,0 @@
1use alloc::rc::Rc;
2use core::cell::UnsafeCell;
3use core::pin::Pin;
4use futures::task::{Context, Poll};
5
6use super::super::error::Result;
7use super::super::traits::{AsyncBufRead, AsyncWrite};
8
9/// The readable half of an object returned from `AsyncBufRead::split`.
10#[derive(Debug)]
11pub struct ReadHalf<T> {
12 handle: Rc<UnsafeCell<T>>,
13}
14
15/// The writable half of an object returned from `AsyncBufRead::split`.
16#[derive(Debug)]
17pub struct WriteHalf<T> {
18 handle: Rc<UnsafeCell<T>>,
19}
20
21impl<T: AsyncBufRead + Unpin> AsyncBufRead for ReadHalf<T> {
22 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
23 Pin::new(unsafe { &mut *self.handle.get() }).poll_fill_buf(cx)
24 }
25
26 fn consume(self: Pin<&mut Self>, amt: usize) {
27 Pin::new(unsafe { &mut *self.handle.get() }).consume(amt)
28 }
29}
30
31impl<T: AsyncWrite + Unpin> AsyncWrite for WriteHalf<T> {
32 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
33 Pin::new(unsafe { &mut *self.handle.get() }).poll_write(cx, buf)
34 }
35 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
36 Pin::new(unsafe { &mut *self.handle.get() }).poll_flush(cx)
37 }
38}
39
40pub fn split<T: AsyncBufRead + AsyncWrite>(t: T) -> (ReadHalf<T>, WriteHalf<T>) {
41 let c = Rc::new(UnsafeCell::new(t));
42 (ReadHalf { handle: c.clone() }, WriteHalf { handle: c })
43}
diff --git a/embassy/src/io/util/write.rs b/embassy/src/io/util/write.rs
deleted file mode 100644
index 403cd59fe..000000000
--- a/embassy/src/io/util/write.rs
+++ /dev/null
@@ -1,33 +0,0 @@
1use core::pin::Pin;
2use futures::future::Future;
3use futures::ready;
4use futures::task::{Context, Poll};
5
6use super::super::error::Result;
7use super::super::traits::AsyncWrite;
8
9/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
10#[derive(Debug)]
11#[must_use = "futures do nothing unless you `.await` or poll them"]
12pub struct Write<'a, W: ?Sized> {
13 writer: &'a mut W,
14 buf: &'a [u8],
15}
16
17impl<W: ?Sized + Unpin> Unpin for Write<'_, W> {}
18
19impl<'a, W: AsyncWrite + ?Sized + Unpin> Write<'a, W> {
20 pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self {
21 Write { writer, buf }
22 }
23}
24
25impl<W: AsyncWrite + ?Sized + Unpin> Future for Write<'_, W> {
26 type Output = Result<usize>;
27
28 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<usize>> {
29 let this = &mut *self;
30 let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?;
31 Poll::Ready(Ok(n))
32 }
33}
diff --git a/embassy/src/io/util/write_all.rs b/embassy/src/io/util/write_all.rs
deleted file mode 100644
index 8a7d9984c..000000000
--- a/embassy/src/io/util/write_all.rs
+++ /dev/null
@@ -1,44 +0,0 @@
1use core::mem;
2use core::pin::Pin;
3use futures::future::Future;
4use futures::ready;
5use futures::task::{Context, Poll};
6
7use super::super::error::Result;
8use super::super::traits::AsyncWrite;
9
10/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
11#[derive(Debug)]
12#[must_use = "futures do nothing unless you `.await` or poll them"]
13pub struct WriteAll<'a, W: ?Sized> {
14 writer: &'a mut W,
15 buf: &'a [u8],
16}
17
18impl<W: ?Sized + Unpin> Unpin for WriteAll<'_, W> {}
19
20impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteAll<'a, W> {
21 pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self {
22 WriteAll { writer, buf }
23 }
24}
25
26impl<W: AsyncWrite + ?Sized + Unpin> Future for WriteAll<'_, W> {
27 type Output = Result<()>;
28
29 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
30 let this = &mut *self;
31 while !this.buf.is_empty() {
32 let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?;
33 {
34 let (_, rest) = mem::take(&mut this.buf).split_at(n);
35 this.buf = rest;
36 }
37 if n == 0 {
38 panic!();
39 }
40 }
41
42 Poll::Ready(Ok(()))
43 }
44}
diff --git a/embassy/src/io/util/write_byte.rs b/embassy/src/io/util/write_byte.rs
deleted file mode 100644
index 659e427b1..000000000
--- a/embassy/src/io/util/write_byte.rs
+++ /dev/null
@@ -1,39 +0,0 @@
1use core::pin::Pin;
2use futures::future::Future;
3use futures::ready;
4use futures::task::{Context, Poll};
5
6use super::super::error::Result;
7use super::super::traits::AsyncWrite;
8
9/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
10#[derive(Debug)]
11#[must_use = "futures do nothing unless you `.await` or poll them"]
12pub struct WriteByte<'a, W: ?Sized> {
13 writer: &'a mut W,
14 byte: u8,
15}
16
17impl<W: ?Sized + Unpin> Unpin for WriteByte<'_, W> {}
18
19impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteByte<'a, W> {
20 pub(super) fn new(writer: &'a mut W, byte: u8) -> Self {
21 WriteByte { writer, byte }
22 }
23}
24
25impl<W: AsyncWrite + ?Sized + Unpin> Future for WriteByte<'_, W> {
26 type Output = Result<()>;
27
28 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
29 let this = &mut *self;
30 let buf = [this.byte; 1];
31 let n = ready!(Pin::new(&mut this.writer).poll_write(cx, &buf))?;
32 if n == 0 {
33 panic!();
34 }
35 assert!(n == 1);
36
37 Poll::Ready(Ok(()))
38 }
39}
diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs
index ec697b40a..087bd357a 100644
--- a/embassy/src/lib.rs
+++ b/embassy/src/lib.rs
@@ -13,7 +13,6 @@ pub mod channel;
13pub mod executor; 13pub mod executor;
14#[cfg(cortex_m)] 14#[cfg(cortex_m)]
15pub mod interrupt; 15pub mod interrupt;
16pub mod io;
17pub mod mutex; 16pub mod mutex;
18#[cfg(feature = "time")] 17#[cfg(feature = "time")]
19pub mod time; 18pub mod time;
diff --git a/examples/boot/stm32f3/.cargo/config.toml b/examples/boot/stm32f3/.cargo/config.toml
new file mode 100644
index 000000000..eb8a8b335
--- /dev/null
+++ b/examples/boot/stm32f3/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32F303VCTx"
4
5[build]
6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32f3/Cargo.toml b/examples/boot/stm32f3/Cargo.toml
new file mode 100644
index 000000000..d4ca600f8
--- /dev/null
+++ b/examples/boot/stm32f3/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2021"
4name = "embassy-boot-stm32f3-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32f3/README.md b/examples/boot/stm32f3/README.md
new file mode 100644
index 000000000..e92ffb692
--- /dev/null
+++ b/examples/boot/stm32f3/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32F3 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f303re --chip STM32F303RETx
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32F303RETx
29```
diff --git a/examples/boot/stm32f3/build.rs b/examples/boot/stm32f3/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32f3/build.rs
@@ -0,0 +1,37 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32f3/memory.x b/examples/boot/stm32f3/memory.x
new file mode 100644
index 000000000..14b2a2c9f
--- /dev/null
+++ b/examples/boot/stm32f3/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 FLASH : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
13
14__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
diff --git a/examples/boot/stm32f3/src/bin/a.rs b/examples/boot/stm32f3/src/bin/a.rs
new file mode 100644
index 000000000..db9262f43
--- /dev/null
+++ b/examples/boot/stm32f3/src/bin/a.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy_boot_stm32::FirmwareUpdater;
6use embassy_stm32::exti::ExtiInput;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
9use embassy_stm32::Peripherals;
10use embassy_traits::adapter::BlockingAsync;
11use panic_reset as _;
12
13#[cfg(feature = "defmt-rtt")]
14use defmt_rtt::*;
15
16static APP_B: &[u8] = include_bytes!("../../b.bin");
17
18#[embassy::main]
19async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
20 let flash = Flash::unlock(p.FLASH);
21 let mut flash = BlockingAsync::new(flash);
22
23 let button = Input::new(p.PC13, Pull::Up);
24 let mut button = ExtiInput::new(button, p.EXTI13);
25
26 let mut led = Output::new(p.PA5, Level::Low, Speed::Low);
27 led.set_high();
28
29 let mut updater = FirmwareUpdater::default();
30 button.wait_for_falling_edge().await;
31 let mut offset = 0;
32 for chunk in APP_B.chunks(2048) {
33 let mut buf: [u8; 2048] = [0; 2048];
34 buf[..chunk.len()].copy_from_slice(chunk);
35 updater
36 .write_firmware(offset, &buf, &mut flash, 2048)
37 .await
38 .unwrap();
39 offset += chunk.len();
40 }
41 updater.update(&mut flash).await.unwrap();
42 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset();
44}
diff --git a/examples/boot/stm32f3/src/bin/b.rs b/examples/boot/stm32f3/src/bin/b.rs
new file mode 100644
index 000000000..814275988
--- /dev/null
+++ b/examples/boot/stm32f3/src/bin/b.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut led = Output::new(p.PA5, Level::High, Speed::Low);
17
18 loop {
19 led.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21
22 led.set_low();
23 Timer::after(Duration::from_millis(500)).await;
24 }
25}
diff --git a/examples/boot/stm32f7/.cargo/config.toml b/examples/boot/stm32f7/.cargo/config.toml
new file mode 100644
index 000000000..df5114520
--- /dev/null
+++ b/examples/boot/stm32f7/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32F767ZITx -v"
4
5[build]
6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32f7/Cargo.toml b/examples/boot/stm32f7/Cargo.toml
new file mode 100644
index 000000000..857b287d5
--- /dev/null
+++ b/examples/boot/stm32f7/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2021"
4name = "embassy-boot-stm32f7-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32f7/README.md b/examples/boot/stm32f7/README.md
new file mode 100644
index 000000000..bf9142a1c
--- /dev/null
+++ b/examples/boot/stm32f7/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32F7 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18./flash-boot.sh
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32F767ZITx
29```
diff --git a/examples/boot/stm32f7/build.rs b/examples/boot/stm32f7/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32f7/build.rs
@@ -0,0 +1,37 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32f7/flash-boot.sh b/examples/boot/stm32f7/flash-boot.sh
new file mode 100755
index 000000000..86074ffa3
--- /dev/null
+++ b/examples/boot/stm32f7/flash-boot.sh
@@ -0,0 +1,8 @@
1#!/bin/bash
2mv ../../../embassy-boot/stm32/memory.x ../../../embassy-boot/stm32/memory-old.x
3cp memory-bl.x ../../../embassy-boot/stm32/memory.x
4
5cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32F767ZITx --target thumbv7em-none-eabihf
6
7rm ../../../embassy-boot/stm32/memory.x
8mv ../../../embassy-boot/stm32/memory-old.x ../../../embassy-boot/stm32/memory.x
diff --git a/examples/boot/stm32f7/memory-bl.x b/examples/boot/stm32f7/memory-bl.x
new file mode 100644
index 000000000..47f3f4d9b
--- /dev/null
+++ b/examples/boot/stm32f7/memory-bl.x
@@ -0,0 +1,18 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 FLASH : ORIGIN = 0x08000000, LENGTH = 256K
5 BOOTLOADER_STATE : ORIGIN = 0x08040000, LENGTH = 256K
6 ACTIVE : ORIGIN = 0x08080000, LENGTH = 256K
7 DFU : ORIGIN = 0x080c0000, LENGTH = 512K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 368K + 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);
13
14__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);
15__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);
16
17__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);
18__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);
diff --git a/examples/boot/stm32f7/memory.x b/examples/boot/stm32f7/memory.x
new file mode 100644
index 000000000..1c5537d17
--- /dev/null
+++ b/examples/boot/stm32f7/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 256K
5 BOOTLOADER_STATE : ORIGIN = 0x08040000, LENGTH = 256K
6 FLASH : ORIGIN = 0x08080000, LENGTH = 256K
7 DFU : ORIGIN = 0x080c0000, LENGTH = 512K
8 RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 368K + 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
13
14__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
diff --git a/examples/boot/stm32f7/src/bin/a.rs b/examples/boot/stm32f7/src/bin/a.rs
new file mode 100644
index 000000000..ca154f0af
--- /dev/null
+++ b/examples/boot/stm32f7/src/bin/a.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy_boot_stm32::FirmwareUpdater;
6use embassy_stm32::exti::ExtiInput;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
9use embassy_stm32::Peripherals;
10use embassy_traits::adapter::BlockingAsync;
11use panic_reset as _;
12
13#[cfg(feature = "defmt-rtt")]
14use defmt_rtt::*;
15
16static APP_B: &[u8] = include_bytes!("../../b.bin");
17
18#[embassy::main]
19async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
20 let flash = Flash::unlock(p.FLASH);
21 let mut flash = BlockingAsync::new(flash);
22
23 let button = Input::new(p.PC13, Pull::Down);
24 let mut button = ExtiInput::new(button, p.EXTI13);
25
26 let mut led = Output::new(p.PB7, Level::Low, Speed::Low);
27 led.set_high();
28
29 let mut updater = FirmwareUpdater::default();
30 button.wait_for_rising_edge().await;
31 let mut offset = 0;
32 let mut buf: [u8; 256 * 1024] = [0; 256 * 1024];
33 for chunk in APP_B.chunks(256 * 1024) {
34 buf[..chunk.len()].copy_from_slice(chunk);
35 updater
36 .write_firmware(offset, &buf, &mut flash, 2048)
37 .await
38 .unwrap();
39 offset += chunk.len();
40 }
41 updater.update(&mut flash).await.unwrap();
42 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset();
44}
diff --git a/examples/boot/stm32f7/src/bin/b.rs b/examples/boot/stm32f7/src/bin/b.rs
new file mode 100644
index 000000000..ed37137f5
--- /dev/null
+++ b/examples/boot/stm32f7/src/bin/b.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 Timer::after(Duration::from_millis(300)).await;
17 let mut led = Output::new(p.PB7, Level::High, Speed::Low);
18 led.set_high();
19
20 loop {
21 led.set_high();
22 Timer::after(Duration::from_millis(500)).await;
23
24 led.set_low();
25 Timer::after(Duration::from_millis(500)).await;
26 }
27}
diff --git a/examples/boot/stm32h7/.cargo/config.toml b/examples/boot/stm32h7/.cargo/config.toml
new file mode 100644
index 000000000..8475e7f66
--- /dev/null
+++ b/examples/boot/stm32h7/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32H743ZITx"
4
5[build]
6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32h7/Cargo.toml b/examples/boot/stm32h7/Cargo.toml
new file mode 100644
index 000000000..1fd03906f
--- /dev/null
+++ b/examples/boot/stm32h7/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2021"
4name = "embassy-boot-stm32f7-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32h7/README.md b/examples/boot/stm32h7/README.md
new file mode 100644
index 000000000..1fdc305e6
--- /dev/null
+++ b/examples/boot/stm32h7/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32H7 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18./flash-boot.sh
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32H743ZITx
29```
diff --git a/examples/boot/stm32h7/build.rs b/examples/boot/stm32h7/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32h7/build.rs
@@ -0,0 +1,37 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32h7/flash-boot.sh b/examples/boot/stm32h7/flash-boot.sh
new file mode 100755
index 000000000..a910b7312
--- /dev/null
+++ b/examples/boot/stm32h7/flash-boot.sh
@@ -0,0 +1,8 @@
1#!/bin/bash
2mv ../../../embassy-boot/stm32/memory.x ../../../embassy-boot/stm32/memory-old.x
3cp memory-bl.x ../../../embassy-boot/stm32/memory.x
4
5cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32H743ZITx --target thumbv7em-none-eabihf
6
7rm ../../../embassy-boot/stm32/memory.x
8mv ../../../embassy-boot/stm32/memory-old.x ../../../embassy-boot/stm32/memory.x
diff --git a/examples/boot/stm32h7/memory-bl.x b/examples/boot/stm32h7/memory-bl.x
new file mode 100644
index 000000000..c6f447d8b
--- /dev/null
+++ b/examples/boot/stm32h7/memory-bl.x
@@ -0,0 +1,18 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 FLASH : ORIGIN = 0x08000000, LENGTH = 128K
5 BOOTLOADER_STATE : ORIGIN = 0x08020000, LENGTH = 128K
6 ACTIVE : ORIGIN = 0x08040000, LENGTH = 128K
7 DFU : ORIGIN = 0x08100000, LENGTH = 512K
8 RAM (rwx) : ORIGIN = 0x24000000, LENGTH = 368K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);
13
14__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);
15__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);
16
17__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);
18__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);
diff --git a/examples/boot/stm32h7/memory.x b/examples/boot/stm32h7/memory.x
new file mode 100644
index 000000000..497a09e41
--- /dev/null
+++ b/examples/boot/stm32h7/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 128K
5 BOOTLOADER_STATE : ORIGIN = 0x08020000, LENGTH = 128K
6 FLASH : ORIGIN = 0x08040000, LENGTH = 256K
7 DFU : ORIGIN = 0x08100000, LENGTH = 512K
8 RAM (rwx) : ORIGIN = 0x24000000, LENGTH = 368K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
13
14__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
diff --git a/examples/boot/stm32h7/src/bin/a.rs b/examples/boot/stm32h7/src/bin/a.rs
new file mode 100644
index 000000000..1f23a8bc2
--- /dev/null
+++ b/examples/boot/stm32h7/src/bin/a.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy_boot_stm32::FirmwareUpdater;
6use embassy_stm32::exti::ExtiInput;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
9use embassy_stm32::Peripherals;
10use embassy_traits::adapter::BlockingAsync;
11use panic_reset as _;
12
13#[cfg(feature = "defmt-rtt")]
14use defmt_rtt::*;
15
16static APP_B: &[u8] = include_bytes!("../../b.bin");
17
18#[embassy::main]
19async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
20 let flash = Flash::unlock(p.FLASH);
21 let mut flash = BlockingAsync::new(flash);
22
23 let button = Input::new(p.PC13, Pull::Down);
24 let mut button = ExtiInput::new(button, p.EXTI13);
25
26 let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
27 led.set_high();
28
29 let mut updater = FirmwareUpdater::default();
30 button.wait_for_rising_edge().await;
31 let mut offset = 0;
32 let mut buf: [u8; 128 * 1024] = [0; 128 * 1024];
33 for chunk in APP_B.chunks(128 * 1024) {
34 buf[..chunk.len()].copy_from_slice(chunk);
35 updater
36 .write_firmware(offset, &buf, &mut flash, 2048)
37 .await
38 .unwrap();
39 offset += chunk.len();
40 }
41 updater.update(&mut flash).await.unwrap();
42 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset();
44}
diff --git a/examples/boot/stm32h7/src/bin/b.rs b/examples/boot/stm32h7/src/bin/b.rs
new file mode 100644
index 000000000..233b93e1a
--- /dev/null
+++ b/examples/boot/stm32h7/src/bin/b.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 Timer::after(Duration::from_millis(300)).await;
17 let mut led = Output::new(p.PB14, Level::High, Speed::Low);
18 led.set_high();
19
20 loop {
21 led.set_high();
22 Timer::after(Duration::from_millis(500)).await;
23
24 led.set_low();
25 Timer::after(Duration::from_millis(500)).await;
26 }
27}
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml
index 4258544f0..ffac0a769 100644
--- a/examples/nrf/Cargo.toml
+++ b/examples/nrf/Cargo.toml
@@ -6,7 +6,7 @@ version = "0.1.0"
6 6
7[features] 7[features]
8default = ["nightly"] 8default = ["nightly"]
9nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm"] 9nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net/nightly"]
10 10
11[dependencies] 11[dependencies]
12embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
@@ -16,6 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm
16embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } 16embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true }
17embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } 17embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true }
18embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true } 18embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true }
19embedded-io = "0.2.0"
19 20
20defmt = "0.3" 21defmt = "0.3"
21defmt-rtt = "0.3" 22defmt-rtt = "0.3"
diff --git a/examples/nrf/src/bin/buffered_uart.rs b/examples/nrf/src/bin/buffered_uart.rs
index 2cd163a9f..a64c5821b 100644
--- a/examples/nrf/src/bin/buffered_uart.rs
+++ b/examples/nrf/src/bin/buffered_uart.rs
@@ -4,9 +4,9 @@
4 4
5use defmt::*; 5use defmt::*;
6use embassy::executor::Spawner; 6use embassy::executor::Spawner;
7use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
8use embassy_nrf::buffered_uarte::State; 7use embassy_nrf::buffered_uarte::State;
9use embassy_nrf::{buffered_uarte::BufferedUarte, interrupt, uarte, Peripherals}; 8use embassy_nrf::{buffered_uarte::BufferedUarte, interrupt, uarte, Peripherals};
9use embedded_io::asynch::{Read, Write};
10use futures::pin_mut; 10use futures::pin_mut;
11 11
12use defmt_rtt as _; // global logger 12use defmt_rtt as _; // global logger
diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs
index f14a29c49..843487c03 100644
--- a/examples/nrf/src/bin/usb_ethernet.rs
+++ b/examples/nrf/src/bin/usb_ethernet.rs
@@ -10,9 +10,9 @@ use defmt::*;
10use embassy::blocking_mutex::raw::ThreadModeRawMutex; 10use embassy::blocking_mutex::raw::ThreadModeRawMutex;
11use embassy::channel::Channel; 11use embassy::channel::Channel;
12use embassy::executor::Spawner; 12use embassy::executor::Spawner;
13use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
14use embassy::util::Forever; 13use embassy::util::Forever;
15use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, TcpSocket}; 14use embassy_net::tcp::TcpSocket;
15use embassy_net::{PacketBox, PacketBoxExt, PacketBuf};
16use embassy_nrf::pac; 16use embassy_nrf::pac;
17use embassy_nrf::usb::Driver; 17use embassy_nrf::usb::Driver;
18use embassy_nrf::Peripherals; 18use embassy_nrf::Peripherals;
@@ -20,7 +20,9 @@ use embassy_nrf::{interrupt, peripherals};
20use embassy_usb::{Builder, Config, UsbDevice}; 20use embassy_usb::{Builder, Config, UsbDevice};
21use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; 21use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
22 22
23use defmt_rtt as _; // global logger 23use defmt_rtt as _;
24use embedded_io::asynch::{Read, Write};
25// global logger
24use panic_probe as _; 26use panic_probe as _;
25 27
26type MyDriver = Driver<'static, peripherals::USBD>; 28type MyDriver = Driver<'static, peripherals::USBD>;
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml
index 0853c323e..fa2a98a49 100644
--- a/examples/std/Cargo.toml
+++ b/examples/std/Cargo.toml
@@ -6,7 +6,8 @@ version = "0.1.0"
6 6
7[dependencies] 7[dependencies]
8embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] } 8embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] }
9embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] } 9embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["nightly", "std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] }
10embedded-io = { version = "0.2.0", features = ["async", "std"] }
10 11
11async-io = "1.6.0" 12async-io = "1.6.0"
12env_logger = "0.9.0" 13env_logger = "0.9.0"
diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs
index 3b4bc6fea..daedffb0f 100644
--- a/examples/std/src/bin/net.rs
+++ b/examples/std/src/bin/net.rs
@@ -2,12 +2,13 @@
2 2
3use clap::Parser; 3use clap::Parser;
4use embassy::executor::{Executor, Spawner}; 4use embassy::executor::{Executor, Spawner};
5use embassy::io::AsyncWriteExt;
6use embassy::util::Forever; 5use embassy::util::Forever;
6use embassy_net::tcp::TcpSocket;
7use embassy_net::{ 7use embassy_net::{
8 Config, Configurator, DhcpConfigurator, Ipv4Address, Ipv4Cidr, StackResources, 8 Config, Configurator, DhcpConfigurator, Ipv4Address, Ipv4Cidr, StackResources,
9 StaticConfigurator, TcpSocket, 9 StaticConfigurator,
10}; 10};
11use embedded_io::asynch::Write;
11use heapless::Vec; 12use heapless::Vec;
12use log::*; 13use log::*;
13 14
diff --git a/examples/std/src/bin/serial.rs b/examples/std/src/bin/serial.rs
index 129dc2090..b1e5b0142 100644
--- a/examples/std/src/bin/serial.rs
+++ b/examples/std/src/bin/serial.rs
@@ -5,8 +5,8 @@ mod serial_port;
5 5
6use async_io::Async; 6use async_io::Async;
7use embassy::executor::Executor; 7use embassy::executor::Executor;
8use embassy::io::AsyncBufReadExt;
9use embassy::util::Forever; 8use embassy::util::Forever;
9use embedded_io::asynch::Read;
10use log::*; 10use log::*;
11use nix::sys::termios; 11use nix::sys::termios;
12 12
@@ -24,12 +24,12 @@ async fn run() {
24 // Essentially, async_io::Async converts from AsRawFd+Read+Write to futures's AsyncRead+AsyncWrite 24 // Essentially, async_io::Async converts from AsRawFd+Read+Write to futures's AsyncRead+AsyncWrite
25 let port = Async::new(port).unwrap(); 25 let port = Async::new(port).unwrap();
26 26
27 // This implements futures's AsyncBufRead based on futures's AsyncRead 27 // We can then use FromStdIo to convert from futures's AsyncRead+AsyncWrite
28 let port = futures::io::BufReader::new(port); 28 // to embedded_io's async Read+Write.
29 29 //
30 // We can then use FromStdIo to convert from futures's AsyncBufRead+AsyncWrite 30 // This is not really needed, you could write the code below using futures::io directly.
31 // to embassy's AsyncBufRead+AsyncWrite 31 // It's useful if you want to have portable code across embedded and std.
32 let mut port = embassy::io::FromStdIo::new(port); 32 let mut port = embedded_io::adapters::FromFutures::new(port);
33 33
34 info!("Serial opened!"); 34 info!("Serial opened!");
35 35
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml
index 37f99fea2..e7a44c11e 100644
--- a/examples/stm32f3/Cargo.toml
+++ b/examples/stm32f3/Cargo.toml
@@ -19,3 +19,4 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
19futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 19futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
20heapless = { version = "0.7.5", default-features = false } 20heapless = { version = "0.7.5", default-features = false }
21nb = "1.0.0" 21nb = "1.0.0"
22embedded-storage = "0.3.0"
diff --git a/examples/stm32f3/src/bin/blinky.rs b/examples/stm32f3/src/bin/blinky.rs
index b3643a26f..4b181a784 100644
--- a/examples/stm32f3/src/bin/blinky.rs
+++ b/examples/stm32f3/src/bin/blinky.rs
@@ -15,7 +15,7 @@ use panic_probe as _;
15async fn main(_spawner: Spawner, p: Peripherals) { 15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello World!"); 16 info!("Hello World!");
17 17
18 let mut led = Output::new(p.PE12, Level::High, Speed::Low); 18 let mut led = Output::new(p.PA5, Level::High, Speed::Low);
19 19
20 loop { 20 loop {
21 info!("high"); 21 info!("high");
diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs
new file mode 100644
index 000000000..3890f0514
--- /dev/null
+++ b/examples/stm32f3/src/bin/flash.rs
@@ -0,0 +1,43 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::Peripherals;
9use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
10
11use defmt_rtt as _; // global logger
12use panic_probe as _;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello Flash!");
17
18 const ADDR: u32 = 0x26000;
19
20 let mut f = Flash::unlock(p.FLASH);
21
22 info!("Reading...");
23 let mut buf = [0u8; 8];
24 unwrap!(f.read(ADDR, &mut buf));
25 info!("Read: {=[u8]:x}", buf);
26
27 info!("Erasing...");
28 unwrap!(f.erase(ADDR, ADDR + 2048));
29
30 info!("Reading...");
31 let mut buf = [0u8; 8];
32 unwrap!(f.read(ADDR, &mut buf));
33 info!("Read after erase: {=[u8]:x}", buf);
34
35 info!("Writing...");
36 unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));
37
38 info!("Reading...");
39 let mut buf = [0u8; 8];
40 unwrap!(f.read(ADDR, &mut buf));
41 info!("Read: {=[u8]:x}", buf);
42 assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);
43}
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/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index 09a06aa7f..f7af77da4 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -9,6 +9,7 @@ resolver = "2"
9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } 10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] }
11embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } 11embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] }
12embedded-io = { version = "0.2.0", features = ["async"] }
12 13
13defmt = "0.3" 14defmt = "0.3"
14defmt-rtt = "0.3" 15defmt-rtt = "0.3"
@@ -22,6 +23,7 @@ heapless = { version = "0.7.5", default-features = false }
22nb = "1.0.0" 23nb = "1.0.0"
23rand_core = "0.6.3" 24rand_core = "0.6.3"
24critical-section = "0.2.3" 25critical-section = "0.2.3"
26embedded-storage = "0.3.0"
25 27
26[dependencies.smoltcp] 28[dependencies.smoltcp]
27version = "0.8.0" 29version = "0.8.0"
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs
index 33e41de9c..dca9338b2 100644
--- a/examples/stm32f7/src/bin/eth.rs
+++ b/examples/stm32f7/src/bin/eth.rs
@@ -5,12 +5,10 @@
5use cortex_m_rt::entry; 5use cortex_m_rt::entry;
6use defmt::*; 6use defmt::*;
7use embassy::executor::{Executor, Spawner}; 7use embassy::executor::{Executor, Spawner};
8use embassy::io::AsyncWriteExt;
9use embassy::time::{Duration, Timer}; 8use embassy::time::{Duration, Timer};
10use embassy::util::Forever; 9use embassy::util::Forever;
11use embassy_net::{ 10use embassy_net::tcp::TcpSocket;
12 Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator, TcpSocket, 11use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator};
13};
14use embassy_stm32::eth::generic_smi::GenericSMI; 12use embassy_stm32::eth::generic_smi::GenericSMI;
15use embassy_stm32::eth::{Ethernet, State}; 13use embassy_stm32::eth::{Ethernet, State};
16use embassy_stm32::interrupt; 14use embassy_stm32::interrupt;
@@ -19,6 +17,7 @@ use embassy_stm32::peripherals::RNG;
19use embassy_stm32::rng::Rng; 17use embassy_stm32::rng::Rng;
20use embassy_stm32::time::U32Ext; 18use embassy_stm32::time::U32Ext;
21use embassy_stm32::Config; 19use embassy_stm32::Config;
20use embedded_io::asynch::Write;
22use heapless::Vec; 21use heapless::Vec;
23 22
24use defmt_rtt as _; // global logger 23use defmt_rtt as _; // global logger
diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs
new file mode 100644
index 000000000..9eb8e4b94
--- /dev/null
+++ b/examples/stm32f7/src/bin/flash.rs
@@ -0,0 +1,59 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy::time::{Duration, Timer};
8use embassy_stm32::flash::Flash;
9use embassy_stm32::Peripherals;
10use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
11
12use defmt_rtt as _; // global logger
13use panic_probe as _;
14
15#[embassy::main]
16async fn main(_spawner: Spawner, p: Peripherals) {
17 info!("Hello Flash!");
18
19 const ADDR: u32 = 0x8_0000;
20
21 // wait a bit before accessing the flash
22 Timer::after(Duration::from_millis(300)).await;
23
24 let mut f = Flash::unlock(p.FLASH);
25
26 info!("Reading...");
27 let mut buf = [0u8; 32];
28 unwrap!(f.read(ADDR, &mut buf));
29 info!("Read: {=[u8]:x}", buf);
30
31 info!("Erasing...");
32 unwrap!(f.erase(ADDR, ADDR + 256 * 1024));
33
34 info!("Reading...");
35 let mut buf = [0u8; 32];
36 unwrap!(f.read(ADDR, &mut buf));
37 info!("Read after erase: {=[u8]:x}", buf);
38
39 info!("Writing...");
40 unwrap!(f.write(
41 ADDR,
42 &[
43 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
44 25, 26, 27, 28, 29, 30, 31, 32
45 ]
46 ));
47
48 info!("Reading...");
49 let mut buf = [0u8; 32];
50 unwrap!(f.read(ADDR, &mut buf));
51 info!("Read: {=[u8]:x}", buf);
52 assert_eq!(
53 &buf[..],
54 &[
55 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
56 25, 26, 27, 28, 29, 30, 31, 32
57 ]
58 );
59}
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index 419bbec31..9c1569dd3 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -5,12 +5,11 @@ name = "embassy-stm32h7-examples"
5version = "0.1.0" 5version = "0.1.0"
6resolver = "2" 6resolver = "2"
7 7
8[features]
9
10[dependencies] 8[dependencies]
11embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } 9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] }
12embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } 10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
13embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } 11embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] }
12embedded-io = { version = "0.2.0", features = ["async"] }
14 13
15defmt = "0.3" 14defmt = "0.3"
16defmt-rtt = "0.3" 15defmt-rtt = "0.3"
@@ -27,6 +26,7 @@ rand_core = "0.6.3"
27critical-section = "0.2.5" 26critical-section = "0.2.5"
28micromath = "2.0.0" 27micromath = "2.0.0"
29stm32-fmc = "0.2.4" 28stm32-fmc = "0.2.4"
29embedded-storage = "0.3.0"
30 30
31[dependencies.smoltcp] 31[dependencies.smoltcp]
32version = "0.8.0" 32version = "0.8.0"
diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs
index 9a2e7a33d..8ece29403 100644
--- a/examples/stm32h7/src/bin/eth.rs
+++ b/examples/stm32h7/src/bin/eth.rs
@@ -8,12 +8,10 @@ use panic_probe as _;
8use cortex_m_rt::entry; 8use cortex_m_rt::entry;
9use defmt::*; 9use defmt::*;
10use embassy::executor::{Executor, Spawner}; 10use embassy::executor::{Executor, Spawner};
11use embassy::io::AsyncWriteExt;
12use embassy::time::{Duration, Timer}; 11use embassy::time::{Duration, Timer};
13use embassy::util::Forever; 12use embassy::util::Forever;
14use embassy_net::{ 13use embassy_net::tcp::TcpSocket;
15 Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator, TcpSocket, 14use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator};
16};
17use embassy_stm32::eth::generic_smi::GenericSMI; 15use embassy_stm32::eth::generic_smi::GenericSMI;
18use embassy_stm32::eth::{Ethernet, State}; 16use embassy_stm32::eth::{Ethernet, State};
19use embassy_stm32::interrupt; 17use embassy_stm32::interrupt;
@@ -22,6 +20,7 @@ use embassy_stm32::peripherals::RNG;
22use embassy_stm32::rng::Rng; 20use embassy_stm32::rng::Rng;
23use embassy_stm32::time::U32Ext; 21use embassy_stm32::time::U32Ext;
24use embassy_stm32::Config; 22use embassy_stm32::Config;
23use embedded_io::asynch::Write;
25use heapless::Vec; 24use heapless::Vec;
26 25
27#[embassy::task] 26#[embassy::task]
diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs
new file mode 100644
index 000000000..b008c0884
--- /dev/null
+++ b/examples/stm32h7/src/bin/flash.rs
@@ -0,0 +1,58 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use defmt_rtt as _; // global logger
7use embassy::executor::Spawner;
8use embassy::time::{Duration, Timer};
9use embassy_stm32::flash::Flash;
10use embassy_stm32::Peripherals;
11use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
12use panic_probe as _;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello Flash!");
17
18 const ADDR: u32 = 0x08_0000;
19
20 // wait a bit before accessing the flash
21 Timer::after(Duration::from_millis(300)).await;
22
23 let mut f = Flash::unlock(p.FLASH);
24
25 info!("Reading...");
26 let mut buf = [0u8; 32];
27 unwrap!(f.read(ADDR, &mut buf));
28 info!("Read: {=[u8]:x}", buf);
29
30 info!("Erasing...");
31 unwrap!(f.erase(ADDR, ADDR + 128 * 1024));
32
33 info!("Reading...");
34 let mut buf = [0u8; 32];
35 unwrap!(f.read(ADDR, &mut buf));
36 info!("Read after erase: {=[u8]:x}", buf);
37
38 info!("Writing...");
39 unwrap!(f.write(
40 ADDR,
41 &[
42 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
43 25, 26, 27, 28, 29, 30, 31, 32
44 ]
45 ));
46
47 info!("Reading...");
48 let mut buf = [0u8; 32];
49 unwrap!(f.read(ADDR, &mut buf));
50 info!("Read: {=[u8]:x}", buf);
51 assert_eq!(
52 &buf[..],
53 &[
54 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
55 25, 26, 27, 28, 29, 30, 31, 32
56 ]
57 );
58}
diff --git a/examples/stm32l0/.cargo/config.toml b/examples/stm32l0/.cargo/config.toml
index 840faa62e..ec0b931f2 100644
--- a/examples/stm32l0/.cargo/config.toml
+++ b/examples/stm32l0/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-run --list-chips` 2# replace your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32L072CZTx" 3runner = "probe-run --chip STM32L053R8Tx"
4 4
5[build] 5[build]
6target = "thumbv6m-none-eabi" 6target = "thumbv6m-none-eabi"
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index 1cdb2f374..d6848c27a 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -7,12 +7,11 @@ resolver = "2"
7 7
8[features] 8[features]
9default = ["nightly"] 9default = ["nightly"]
10nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan"] 10nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", "embedded-io/async"]
11 11
12[dependencies] 12[dependencies]
13embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } 13embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
14embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } 14embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
15
16embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} 15embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true}
17 16
18lorawan-device = { version = "0.7.1", default-features = false, features = ["async"], optional = true } 17lorawan-device = { version = "0.7.1", default-features = false, features = ["async"], optional = true }
@@ -22,6 +21,7 @@ defmt = "0.3"
22defmt-rtt = "0.3" 21defmt-rtt = "0.3"
23 22
24embedded-storage = "0.3.0" 23embedded-storage = "0.3.0"
24embedded-io = "0.2.0"
25 25
26cortex-m = "0.7.3" 26cortex-m = "0.7.3"
27cortex-m-rt = "0.7.0" 27cortex-m-rt = "0.7.0"
diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs
index abb27fa4f..4413a2945 100644
--- a/examples/stm32l0/src/bin/usart_irq.rs
+++ b/examples/stm32l0/src/bin/usart_irq.rs
@@ -2,13 +2,14 @@
2#![no_main] 2#![no_main]
3#![feature(type_alias_impl_trait)] 3#![feature(type_alias_impl_trait)]
4 4
5use defmt_rtt as _; // global logger 5use defmt_rtt as _;
6use embedded_io::asynch::{Read, Write};
7// global logger
6use panic_probe as _; 8use panic_probe as _;
7 9
8use defmt::*; 10use defmt::*;
9 11
10use embassy::executor::Spawner; 12use embassy::executor::Spawner;
11use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
12use embassy_stm32::dma::NoDma; 13use embassy_stm32::dma::NoDma;
13use embassy_stm32::interrupt; 14use embassy_stm32::interrupt;
14use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; 15use embassy_stm32::usart::{BufferedUart, Config, State, Uart};
@@ -16,19 +17,21 @@ use embassy_stm32::Peripherals;
16 17
17#[embassy::main] 18#[embassy::main]
18async fn main(_spawner: Spawner, p: Peripherals) { 19async fn main(_spawner: Spawner, p: Peripherals) {
20 info!("Hi!");
21
19 static mut TX_BUFFER: [u8; 8] = [0; 8]; 22 static mut TX_BUFFER: [u8; 8] = [0; 8];
20 static mut RX_BUFFER: [u8; 256] = [0; 256]; 23 static mut RX_BUFFER: [u8; 256] = [0; 256];
21 24
22 let mut config = Config::default(); 25 let mut config = Config::default();
23 config.baudrate = 9600; 26 config.baudrate = 9600;
24 27
25 let usart = Uart::new(p.USART1, p.PA10, p.PA9, NoDma, NoDma, config); 28 let usart = Uart::new(p.USART2, p.PA3, p.PA2, NoDma, NoDma, config);
26 let mut state = State::new(); 29 let mut state = State::new();
27 let mut usart = unsafe { 30 let mut usart = unsafe {
28 BufferedUart::new( 31 BufferedUart::new(
29 &mut state, 32 &mut state,
30 usart, 33 usart,
31 interrupt::take!(USART1), 34 interrupt::take!(USART2),
32 &mut TX_BUFFER, 35 &mut TX_BUFFER,
33 &mut RX_BUFFER, 36 &mut RX_BUFFER,
34 ) 37 )
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}
diff --git a/stm32-data b/stm32-data
Subproject 9abfa9d2b51e6071fdc7e680b4a171e4fa20c2f Subproject b2d7a9f5de7dc3ae17c87c1ff94e13a822d18e7