diff options
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 | |||
| 14 | The bootloader supports | 14 | The 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 | ||
| 19 | In 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. | 19 | In 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 @@ | |||
| 2 | authors = [ | 2 | authors = [ |
| 3 | "Ulf Lilleengen <[email protected]>", | 3 | "Ulf Lilleengen <[email protected]>", |
| 4 | ] | 4 | ] |
| 5 | edition = "2018" | 5 | edition = "2021" |
| 6 | name = "embassy-boot-stm32" | 6 | name = "embassy-boot-stm32" |
| 7 | version = "0.1.0" | 7 | version = "0.1.0" |
| 8 | description = "Bootloader for STM32 chips" | 8 | description = "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" } | |||
| 12 | defmt = { version = "0.3", optional = true } | 12 | defmt = { version = "0.3", optional = true } |
| 13 | log = { version = "0.4.14", optional = true } | 13 | log = { version = "0.4.14", optional = true } |
| 14 | cortex-m = "0.7.3" | 14 | cortex-m = "0.7.3" |
| 15 | usb-device = "0.2.8" | ||
| 16 | num-traits = { version = "0.2.14", default-features = false } | 15 | num-traits = { version = "0.2.14", default-features = false } |
diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index 1af30c6b4..6ee2ccd59 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs | |||
| @@ -10,7 +10,6 @@ mod macros; | |||
| 10 | pub mod peripheral; | 10 | pub mod peripheral; |
| 11 | pub mod ratio; | 11 | pub mod ratio; |
| 12 | pub mod ring_buffer; | 12 | pub mod ring_buffer; |
| 13 | pub mod usb; | ||
| 14 | 13 | ||
| 15 | /// Low power blocking wait loop using WFE/SEV. | 14 | /// Low power blocking wait loop using WFE/SEV. |
| 16 | pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) { | 15 | pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) { |
diff --git a/embassy-hal-common/src/usb/cdc_acm.rs b/embassy-hal-common/src/usb/cdc_acm.rs deleted file mode 100644 index 5a85b3846..000000000 --- a/embassy-hal-common/src/usb/cdc_acm.rs +++ /dev/null | |||
| @@ -1,338 +0,0 @@ | |||
| 1 | // Copied from https://github.com/mvirkkunen/usbd-serial | ||
| 2 | #![allow(dead_code)] | ||
| 3 | |||
| 4 | use core::convert::TryInto; | ||
| 5 | use core::mem; | ||
| 6 | use usb_device::class_prelude::*; | ||
| 7 | use usb_device::Result; | ||
| 8 | |||
| 9 | /// This should be used as `device_class` when building the `UsbDevice`. | ||
| 10 | pub const USB_CLASS_CDC: u8 = 0x02; | ||
| 11 | |||
| 12 | const USB_CLASS_CDC_DATA: u8 = 0x0a; | ||
| 13 | const CDC_SUBCLASS_ACM: u8 = 0x02; | ||
| 14 | const CDC_PROTOCOL_NONE: u8 = 0x00; | ||
| 15 | |||
| 16 | const CS_INTERFACE: u8 = 0x24; | ||
| 17 | const CDC_TYPE_HEADER: u8 = 0x00; | ||
| 18 | const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01; | ||
| 19 | const CDC_TYPE_ACM: u8 = 0x02; | ||
| 20 | const CDC_TYPE_UNION: u8 = 0x06; | ||
| 21 | |||
| 22 | const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00; | ||
| 23 | #[allow(unused)] | ||
| 24 | const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01; | ||
| 25 | const REQ_SET_LINE_CODING: u8 = 0x20; | ||
| 26 | const REQ_GET_LINE_CODING: u8 = 0x21; | ||
| 27 | const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | ||
| 28 | |||
| 29 | /// Packet level implementation of a CDC-ACM serial port. | ||
| 30 | /// | ||
| 31 | /// This class can be used directly and it has the least overhead due to directly reading and | ||
| 32 | /// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial | ||
| 33 | /// port. The following constraints must be followed if you use this class directly: | ||
| 34 | /// | ||
| 35 | /// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the | ||
| 36 | /// method will return a `WouldBlock` error if there is no packet to be read. | ||
| 37 | /// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the | ||
| 38 | /// method will return a `WouldBlock` error if the previous packet has not been sent yet. | ||
| 39 | /// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the | ||
| 40 | /// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP) | ||
| 41 | /// can be sent if there is no other data to send. This is because USB bulk transactions must be | ||
| 42 | /// terminated with a short packet, even if the bulk endpoint is used for stream-like data. | ||
| 43 | pub struct CdcAcmClass<'a, B: UsbBus> { | ||
| 44 | comm_if: InterfaceNumber, | ||
| 45 | comm_ep: EndpointIn<'a, B>, | ||
| 46 | data_if: InterfaceNumber, | ||
| 47 | read_ep: EndpointOut<'a, B>, | ||
| 48 | write_ep: EndpointIn<'a, B>, | ||
| 49 | line_coding: LineCoding, | ||
| 50 | dtr: bool, | ||
| 51 | rts: bool, | ||
| 52 | } | ||
| 53 | |||
| 54 | impl<B: UsbBus> CdcAcmClass<'_, B> { | ||
| 55 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For | ||
| 56 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. | ||
| 57 | pub fn new(alloc: &UsbBusAllocator<B>, max_packet_size: u16) -> CdcAcmClass<'_, B> { | ||
| 58 | CdcAcmClass { | ||
| 59 | comm_if: alloc.interface(), | ||
| 60 | comm_ep: alloc.interrupt(8, 255), | ||
| 61 | data_if: alloc.interface(), | ||
| 62 | read_ep: alloc.bulk(max_packet_size), | ||
| 63 | write_ep: alloc.bulk(max_packet_size), | ||
| 64 | line_coding: LineCoding { | ||
| 65 | stop_bits: StopBits::One, | ||
| 66 | data_bits: 8, | ||
| 67 | parity_type: ParityType::None, | ||
| 68 | data_rate: 8_000, | ||
| 69 | }, | ||
| 70 | dtr: false, | ||
| 71 | rts: false, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Gets the maximum packet size in bytes. | ||
| 76 | pub fn max_packet_size(&self) -> u16 { | ||
| 77 | // The size is the same for both endpoints. | ||
| 78 | self.read_ep.max_packet_size() | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Gets the current line coding. The line coding contains information that's mainly relevant | ||
| 82 | /// for USB to UART serial port emulators, and can be ignored if not relevant. | ||
| 83 | pub fn line_coding(&self) -> &LineCoding { | ||
| 84 | &self.line_coding | ||
| 85 | } | ||
| 86 | |||
| 87 | /// Gets the DTR (data terminal ready) state | ||
| 88 | pub fn dtr(&self) -> bool { | ||
| 89 | self.dtr | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Gets the RTS (request to send) state | ||
| 93 | pub fn rts(&self) -> bool { | ||
| 94 | self.rts | ||
| 95 | } | ||
| 96 | |||
| 97 | /// Writes a single packet into the IN endpoint. | ||
| 98 | pub fn write_packet(&mut self, data: &[u8]) -> Result<usize> { | ||
| 99 | self.write_ep.write(data) | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Reads a single packet from the OUT endpoint. | ||
| 103 | pub fn read_packet(&mut self, data: &mut [u8]) -> Result<usize> { | ||
| 104 | self.read_ep.read(data) | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Gets the address of the IN endpoint. | ||
| 108 | pub fn write_ep_address(&self) -> EndpointAddress { | ||
| 109 | self.write_ep.address() | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Gets the address of the OUT endpoint. | ||
| 113 | pub fn read_ep_address(&self) -> EndpointAddress { | ||
| 114 | self.read_ep.address() | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> { | ||
| 119 | fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { | ||
| 120 | writer.iad( | ||
| 121 | self.comm_if, | ||
| 122 | 2, | ||
| 123 | USB_CLASS_CDC, | ||
| 124 | CDC_SUBCLASS_ACM, | ||
| 125 | CDC_PROTOCOL_NONE, | ||
| 126 | )?; | ||
| 127 | |||
| 128 | writer.interface( | ||
| 129 | self.comm_if, | ||
| 130 | USB_CLASS_CDC, | ||
| 131 | CDC_SUBCLASS_ACM, | ||
| 132 | CDC_PROTOCOL_NONE, | ||
| 133 | )?; | ||
| 134 | |||
| 135 | writer.write( | ||
| 136 | CS_INTERFACE, | ||
| 137 | &[ | ||
| 138 | CDC_TYPE_HEADER, // bDescriptorSubtype | ||
| 139 | 0x10, | ||
| 140 | 0x01, // bcdCDC (1.10) | ||
| 141 | ], | ||
| 142 | )?; | ||
| 143 | |||
| 144 | writer.write( | ||
| 145 | CS_INTERFACE, | ||
| 146 | &[ | ||
| 147 | CDC_TYPE_ACM, // bDescriptorSubtype | ||
| 148 | 0x00, // bmCapabilities | ||
| 149 | ], | ||
| 150 | )?; | ||
| 151 | |||
| 152 | writer.write( | ||
| 153 | CS_INTERFACE, | ||
| 154 | &[ | ||
| 155 | CDC_TYPE_UNION, // bDescriptorSubtype | ||
| 156 | self.comm_if.into(), // bControlInterface | ||
| 157 | self.data_if.into(), // bSubordinateInterface | ||
| 158 | ], | ||
| 159 | )?; | ||
| 160 | |||
| 161 | writer.write( | ||
| 162 | CS_INTERFACE, | ||
| 163 | &[ | ||
| 164 | CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype | ||
| 165 | 0x00, // bmCapabilities | ||
| 166 | self.data_if.into(), // bDataInterface | ||
| 167 | ], | ||
| 168 | )?; | ||
| 169 | |||
| 170 | writer.endpoint(&self.comm_ep)?; | ||
| 171 | |||
| 172 | writer.interface(self.data_if, USB_CLASS_CDC_DATA, 0x00, 0x00)?; | ||
| 173 | |||
| 174 | writer.endpoint(&self.write_ep)?; | ||
| 175 | writer.endpoint(&self.read_ep)?; | ||
| 176 | |||
| 177 | Ok(()) | ||
| 178 | } | ||
| 179 | |||
| 180 | fn reset(&mut self) { | ||
| 181 | self.line_coding = LineCoding::default(); | ||
| 182 | self.dtr = false; | ||
| 183 | self.rts = false; | ||
| 184 | } | ||
| 185 | |||
| 186 | fn control_in(&mut self, xfer: ControlIn<B>) { | ||
| 187 | let req = xfer.request(); | ||
| 188 | |||
| 189 | if !(req.request_type == control::RequestType::Class | ||
| 190 | && req.recipient == control::Recipient::Interface | ||
| 191 | && req.index == u8::from(self.comm_if) as u16) | ||
| 192 | { | ||
| 193 | return; | ||
| 194 | } | ||
| 195 | |||
| 196 | match req.request { | ||
| 197 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. | ||
| 198 | REQ_GET_LINE_CODING if req.length == 7 => { | ||
| 199 | xfer.accept(|data| { | ||
| 200 | data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); | ||
| 201 | data[4] = self.line_coding.stop_bits as u8; | ||
| 202 | data[5] = self.line_coding.parity_type as u8; | ||
| 203 | data[6] = self.line_coding.data_bits; | ||
| 204 | |||
| 205 | Ok(7) | ||
| 206 | }) | ||
| 207 | .ok(); | ||
| 208 | } | ||
| 209 | _ => { | ||
| 210 | xfer.reject().ok(); | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | fn control_out(&mut self, xfer: ControlOut<B>) { | ||
| 216 | let req = xfer.request(); | ||
| 217 | |||
| 218 | if !(req.request_type == control::RequestType::Class | ||
| 219 | && req.recipient == control::Recipient::Interface | ||
| 220 | && req.index == u8::from(self.comm_if) as u16) | ||
| 221 | { | ||
| 222 | return; | ||
| 223 | } | ||
| 224 | |||
| 225 | match req.request { | ||
| 226 | REQ_SEND_ENCAPSULATED_COMMAND => { | ||
| 227 | // We don't actually support encapsulated commands but pretend we do for standards | ||
| 228 | // compatibility. | ||
| 229 | xfer.accept().ok(); | ||
| 230 | } | ||
| 231 | REQ_SET_LINE_CODING if xfer.data().len() >= 7 => { | ||
| 232 | self.line_coding.data_rate = | ||
| 233 | u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap()); | ||
| 234 | self.line_coding.stop_bits = xfer.data()[4].into(); | ||
| 235 | self.line_coding.parity_type = xfer.data()[5].into(); | ||
| 236 | self.line_coding.data_bits = xfer.data()[6]; | ||
| 237 | |||
| 238 | xfer.accept().ok(); | ||
| 239 | } | ||
| 240 | REQ_SET_CONTROL_LINE_STATE => { | ||
| 241 | self.dtr = (req.value & 0x0001) != 0; | ||
| 242 | self.rts = (req.value & 0x0002) != 0; | ||
| 243 | |||
| 244 | xfer.accept().ok(); | ||
| 245 | } | ||
| 246 | _ => { | ||
| 247 | xfer.reject().ok(); | ||
| 248 | } | ||
| 249 | }; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | /// Number of stop bits for LineCoding | ||
| 254 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
| 255 | pub enum StopBits { | ||
| 256 | /// 1 stop bit | ||
| 257 | One = 0, | ||
| 258 | |||
| 259 | /// 1.5 stop bits | ||
| 260 | OnePointFive = 1, | ||
| 261 | |||
| 262 | /// 2 stop bits | ||
| 263 | Two = 2, | ||
| 264 | } | ||
| 265 | |||
| 266 | impl From<u8> for StopBits { | ||
| 267 | fn from(value: u8) -> Self { | ||
| 268 | if value <= 2 { | ||
| 269 | unsafe { mem::transmute(value) } | ||
| 270 | } else { | ||
| 271 | StopBits::One | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Parity for LineCoding | ||
| 277 | #[derive(Copy, Clone, PartialEq, Eq)] | ||
| 278 | pub enum ParityType { | ||
| 279 | None = 0, | ||
| 280 | Odd = 1, | ||
| 281 | Event = 2, | ||
| 282 | Mark = 3, | ||
| 283 | Space = 4, | ||
| 284 | } | ||
| 285 | |||
| 286 | impl From<u8> for ParityType { | ||
| 287 | fn from(value: u8) -> Self { | ||
| 288 | if value <= 4 { | ||
| 289 | unsafe { mem::transmute(value) } | ||
| 290 | } else { | ||
| 291 | ParityType::None | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | /// Line coding parameters | ||
| 297 | /// | ||
| 298 | /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can | ||
| 299 | /// be ignored if you don't plan to interface with a physical UART. | ||
| 300 | pub struct LineCoding { | ||
| 301 | stop_bits: StopBits, | ||
| 302 | data_bits: u8, | ||
| 303 | parity_type: ParityType, | ||
| 304 | data_rate: u32, | ||
| 305 | } | ||
| 306 | |||
| 307 | impl LineCoding { | ||
| 308 | /// Gets the number of stop bits for UART communication. | ||
| 309 | pub fn stop_bits(&self) -> StopBits { | ||
| 310 | self.stop_bits | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Gets the number of data bits for UART communication. | ||
| 314 | pub fn data_bits(&self) -> u8 { | ||
| 315 | self.data_bits | ||
| 316 | } | ||
| 317 | |||
| 318 | /// Gets the parity type for UART communication. | ||
| 319 | pub fn parity_type(&self) -> ParityType { | ||
| 320 | self.parity_type | ||
| 321 | } | ||
| 322 | |||
| 323 | /// Gets the data rate in bits per second for UART communication. | ||
| 324 | pub fn data_rate(&self) -> u32 { | ||
| 325 | self.data_rate | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | impl Default for LineCoding { | ||
| 330 | fn default() -> Self { | ||
| 331 | LineCoding { | ||
| 332 | stop_bits: StopBits::One, | ||
| 333 | data_bits: 8, | ||
| 334 | parity_type: ParityType::None, | ||
| 335 | data_rate: 8_000, | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
diff --git a/embassy-hal-common/src/usb/mod.rs b/embassy-hal-common/src/usb/mod.rs deleted file mode 100644 index bab72d8b6..000000000 --- a/embassy-hal-common/src/usb/mod.rs +++ /dev/null | |||
| @@ -1,267 +0,0 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::pin::Pin; | ||
| 4 | |||
| 5 | use usb_device::bus::UsbBus; | ||
| 6 | use usb_device::class::UsbClass; | ||
| 7 | use usb_device::device::UsbDevice; | ||
| 8 | |||
| 9 | mod cdc_acm; | ||
| 10 | pub mod usb_serial; | ||
| 11 | |||
| 12 | use crate::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; | ||
| 13 | use embassy::interrupt::Interrupt; | ||
| 14 | pub use usb_serial::{ReadInterface, UsbSerial, WriteInterface}; | ||
| 15 | |||
| 16 | /// Marker trait to mark an interrupt to be used with the [`Usb`] abstraction. | ||
| 17 | pub unsafe trait USBInterrupt: Interrupt + Send {} | ||
| 18 | |||
| 19 | pub struct State<'bus, B, T, I>(StateStorage<StateInner<'bus, B, T, I>>) | ||
| 20 | where | ||
| 21 | B: UsbBus, | ||
| 22 | T: ClassSet<B>, | ||
| 23 | I: USBInterrupt; | ||
| 24 | |||
| 25 | impl<'bus, B, T, I> State<'bus, B, T, I> | ||
| 26 | where | ||
| 27 | B: UsbBus, | ||
| 28 | T: ClassSet<B>, | ||
| 29 | I: USBInterrupt, | ||
| 30 | { | ||
| 31 | pub fn new() -> Self { | ||
| 32 | Self(StateStorage::new()) | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | pub(crate) struct StateInner<'bus, B, T, I> | ||
| 37 | where | ||
| 38 | B: UsbBus, | ||
| 39 | T: ClassSet<B>, | ||
| 40 | I: USBInterrupt, | ||
| 41 | { | ||
| 42 | device: UsbDevice<'bus, B>, | ||
| 43 | pub(crate) classes: T, | ||
| 44 | _interrupt: PhantomData<I>, | ||
| 45 | } | ||
| 46 | |||
| 47 | pub struct Usb<'bus, B, T, I> | ||
| 48 | where | ||
| 49 | B: UsbBus, | ||
| 50 | T: ClassSet<B>, | ||
| 51 | I: USBInterrupt, | ||
| 52 | { | ||
| 53 | // Don't you dare moving out `PeripheralMutex` | ||
| 54 | inner: RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, I>>>, | ||
| 55 | } | ||
| 56 | |||
| 57 | impl<'bus, B, T, I> Usb<'bus, B, T, I> | ||
| 58 | where | ||
| 59 | B: UsbBus, | ||
| 60 | T: ClassSet<B>, | ||
| 61 | I: USBInterrupt, | ||
| 62 | { | ||
| 63 | /// safety: the returned instance is not leak-safe | ||
| 64 | pub unsafe fn new<S: IntoClassSet<B, T>>( | ||
| 65 | state: &'bus mut State<'bus, B, T, I>, | ||
| 66 | device: UsbDevice<'bus, B>, | ||
| 67 | class_set: S, | ||
| 68 | irq: I, | ||
| 69 | ) -> Self { | ||
| 70 | let mutex = PeripheralMutex::new_unchecked(irq, &mut state.0, || StateInner { | ||
| 71 | device, | ||
| 72 | classes: class_set.into_class_set(), | ||
| 73 | _interrupt: PhantomData, | ||
| 74 | }); | ||
| 75 | Self { | ||
| 76 | inner: RefCell::new(mutex), | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I> | ||
| 82 | where | ||
| 83 | B: UsbBus, | ||
| 84 | T: ClassSet<B> + SerialState<'bus, 'c, B, Index0>, | ||
| 85 | I: USBInterrupt, | ||
| 86 | { | ||
| 87 | /// Take a serial class that was passed as the first class in a tuple | ||
| 88 | pub fn take_serial_0<'a>( | ||
| 89 | self: Pin<&'a Self>, | ||
| 90 | ) -> ( | ||
| 91 | ReadInterface<'a, 'bus, 'c, Index0, B, T, I>, | ||
| 92 | WriteInterface<'a, 'bus, 'c, Index0, B, T, I>, | ||
| 93 | ) { | ||
| 94 | let this = self.get_ref(); | ||
| 95 | |||
| 96 | let r = ReadInterface { | ||
| 97 | inner: &this.inner, | ||
| 98 | _buf_lifetime: PhantomData, | ||
| 99 | _index: PhantomData, | ||
| 100 | }; | ||
| 101 | |||
| 102 | let w = WriteInterface { | ||
| 103 | inner: &this.inner, | ||
| 104 | _buf_lifetime: PhantomData, | ||
| 105 | _index: PhantomData, | ||
| 106 | }; | ||
| 107 | (r, w) | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I> | ||
| 112 | where | ||
| 113 | B: UsbBus, | ||
| 114 | T: ClassSet<B> + SerialState<'bus, 'c, B, Index1>, | ||
| 115 | I: USBInterrupt, | ||
| 116 | { | ||
| 117 | /// Take a serial class that was passed as the second class in a tuple | ||
| 118 | pub fn take_serial_1<'a>( | ||
| 119 | self: Pin<&'a Self>, | ||
| 120 | ) -> ( | ||
| 121 | ReadInterface<'a, 'bus, 'c, Index1, B, T, I>, | ||
| 122 | WriteInterface<'a, 'bus, 'c, Index1, B, T, I>, | ||
| 123 | ) { | ||
| 124 | let this = self.get_ref(); | ||
| 125 | |||
| 126 | let r = ReadInterface { | ||
| 127 | inner: &this.inner, | ||
| 128 | _buf_lifetime: PhantomData, | ||
| 129 | _index: PhantomData, | ||
| 130 | }; | ||
| 131 | |||
| 132 | let w = WriteInterface { | ||
| 133 | inner: &this.inner, | ||
| 134 | _buf_lifetime: PhantomData, | ||
| 135 | _index: PhantomData, | ||
| 136 | }; | ||
| 137 | (r, w) | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | impl<'bus, B, T, I> PeripheralState for StateInner<'bus, B, T, I> | ||
| 142 | where | ||
| 143 | B: UsbBus, | ||
| 144 | T: ClassSet<B>, | ||
| 145 | I: USBInterrupt, | ||
| 146 | { | ||
| 147 | type Interrupt = I; | ||
| 148 | fn on_interrupt(&mut self) { | ||
| 149 | self.classes.poll_all(&mut self.device); | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | pub trait ClassSet<B: UsbBus>: Send { | ||
| 154 | fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool; | ||
| 155 | } | ||
| 156 | |||
| 157 | pub trait IntoClassSet<B: UsbBus, C: ClassSet<B>> { | ||
| 158 | fn into_class_set(self) -> C; | ||
| 159 | } | ||
| 160 | |||
| 161 | pub struct ClassSet1<B, C1> | ||
| 162 | where | ||
| 163 | B: UsbBus, | ||
| 164 | C1: UsbClass<B>, | ||
| 165 | { | ||
| 166 | class: C1, | ||
| 167 | _bus: PhantomData<B>, | ||
| 168 | } | ||
| 169 | |||
| 170 | pub struct ClassSet2<B, C1, C2> | ||
| 171 | where | ||
| 172 | B: UsbBus, | ||
| 173 | C1: UsbClass<B>, | ||
| 174 | C2: UsbClass<B>, | ||
| 175 | { | ||
| 176 | class1: C1, | ||
| 177 | class2: C2, | ||
| 178 | _bus: PhantomData<B>, | ||
| 179 | } | ||
| 180 | |||
| 181 | /// The first class into a [`ClassSet`] | ||
| 182 | pub struct Index0; | ||
| 183 | |||
| 184 | /// The second class into a [`ClassSet`] | ||
| 185 | pub struct Index1; | ||
| 186 | |||
| 187 | impl<B, C1> ClassSet<B> for ClassSet1<B, C1> | ||
| 188 | where | ||
| 189 | B: UsbBus + Send, | ||
| 190 | C1: UsbClass<B> + Send, | ||
| 191 | { | ||
| 192 | fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { | ||
| 193 | device.poll(&mut [&mut self.class]) | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | impl<B, C1, C2> ClassSet<B> for ClassSet2<B, C1, C2> | ||
| 198 | where | ||
| 199 | B: UsbBus + Send, | ||
| 200 | C1: UsbClass<B> + Send, | ||
| 201 | C2: UsbClass<B> + Send, | ||
| 202 | { | ||
| 203 | fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { | ||
| 204 | device.poll(&mut [&mut self.class1, &mut self.class2]) | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | impl<B, C1> IntoClassSet<B, ClassSet1<B, C1>> for C1 | ||
| 209 | where | ||
| 210 | B: UsbBus + Send, | ||
| 211 | C1: UsbClass<B> + Send, | ||
| 212 | { | ||
| 213 | fn into_class_set(self) -> ClassSet1<B, C1> { | ||
| 214 | ClassSet1 { | ||
| 215 | class: self, | ||
| 216 | _bus: PhantomData, | ||
| 217 | } | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | impl<B, C1, C2> IntoClassSet<B, ClassSet2<B, C1, C2>> for (C1, C2) | ||
| 222 | where | ||
| 223 | B: UsbBus + Send, | ||
| 224 | C1: UsbClass<B> + Send, | ||
| 225 | C2: UsbClass<B> + Send, | ||
| 226 | { | ||
| 227 | fn into_class_set(self) -> ClassSet2<B, C1, C2> { | ||
| 228 | ClassSet2 { | ||
| 229 | class1: self.0, | ||
| 230 | class2: self.1, | ||
| 231 | _bus: PhantomData, | ||
| 232 | } | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Trait for a USB State that has a serial class inside | ||
| 237 | pub trait SerialState<'bus, 'a, B: UsbBus, I> { | ||
| 238 | fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B>; | ||
| 239 | } | ||
| 240 | |||
| 241 | impl<'bus, 'a, B: UsbBus> SerialState<'bus, 'a, B, Index0> | ||
| 242 | for ClassSet1<B, UsbSerial<'bus, 'a, B>> | ||
| 243 | { | ||
| 244 | fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { | ||
| 245 | &mut self.class | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | impl<'bus, 'a, B, C2> SerialState<'bus, 'a, B, Index0> for ClassSet2<B, UsbSerial<'bus, 'a, B>, C2> | ||
| 250 | where | ||
| 251 | B: UsbBus, | ||
| 252 | C2: UsbClass<B>, | ||
| 253 | { | ||
| 254 | fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { | ||
| 255 | &mut self.class1 | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | impl<'bus, 'a, B, C1> SerialState<'bus, 'a, B, Index1> for ClassSet2<B, C1, UsbSerial<'bus, 'a, B>> | ||
| 260 | where | ||
| 261 | B: UsbBus, | ||
| 262 | C1: UsbClass<B>, | ||
| 263 | { | ||
| 264 | fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { | ||
| 265 | &mut self.class2 | ||
| 266 | } | ||
| 267 | } | ||
diff --git a/embassy-hal-common/src/usb/usb_serial.rs b/embassy-hal-common/src/usb/usb_serial.rs deleted file mode 100644 index 94f687890..000000000 --- a/embassy-hal-common/src/usb/usb_serial.rs +++ /dev/null | |||
| @@ -1,345 +0,0 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::marker::{PhantomData, Unpin}; | ||
| 3 | use core::pin::Pin; | ||
| 4 | use core::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use embassy::io::{self, AsyncBufRead, AsyncWrite}; | ||
| 7 | use embassy::waitqueue::WakerRegistration; | ||
| 8 | use usb_device::bus::UsbBus; | ||
| 9 | use usb_device::class_prelude::*; | ||
| 10 | use usb_device::UsbError; | ||
| 11 | |||
| 12 | use super::cdc_acm::CdcAcmClass; | ||
| 13 | use super::StateInner; | ||
| 14 | use crate::peripheral::PeripheralMutex; | ||
| 15 | use crate::ring_buffer::RingBuffer; | ||
| 16 | use crate::usb::{ClassSet, SerialState, USBInterrupt}; | ||
| 17 | |||
| 18 | pub struct ReadInterface<'a, 'bus, 'c, I, B, T, INT> | ||
| 19 | where | ||
| 20 | I: Unpin, | ||
| 21 | B: UsbBus, | ||
| 22 | T: SerialState<'bus, 'c, B, I> + ClassSet<B>, | ||
| 23 | INT: USBInterrupt, | ||
| 24 | { | ||
| 25 | // Don't you dare moving out `PeripheralMutex` | ||
| 26 | pub(crate) inner: &'a RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, INT>>>, | ||
| 27 | pub(crate) _buf_lifetime: PhantomData<&'c T>, | ||
| 28 | pub(crate) _index: PhantomData<I>, | ||
| 29 | } | ||
| 30 | |||
| 31 | /// Write interface for USB CDC_ACM | ||
| 32 | /// | ||
| 33 | /// This interface is buffered, meaning that after the write returns the bytes might not be fully | ||
| 34 | /// on the wire just yet | ||
| 35 | pub struct WriteInterface<'a, 'bus, 'c, I, B, T, INT> | ||
| 36 | where | ||
| 37 | I: Unpin, | ||
| 38 | B: UsbBus, | ||
| 39 | T: SerialState<'bus, 'c, B, I> + ClassSet<B>, | ||
| 40 | INT: USBInterrupt, | ||
| 41 | { | ||
| 42 | // Don't you dare moving out `PeripheralMutex` | ||
| 43 | pub(crate) inner: &'a RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, INT>>>, | ||
| 44 | pub(crate) _buf_lifetime: PhantomData<&'c T>, | ||
| 45 | pub(crate) _index: PhantomData<I>, | ||
| 46 | } | ||
| 47 | |||
| 48 | impl<'a, 'bus, 'c, I, B, T, INT> AsyncBufRead for ReadInterface<'a, 'bus, 'c, I, B, T, INT> | ||
| 49 | where | ||
| 50 | I: Unpin, | ||
| 51 | B: UsbBus, | ||
| 52 | T: SerialState<'bus, 'c, B, I> + ClassSet<B>, | ||
| 53 | INT: USBInterrupt, | ||
| 54 | { | ||
| 55 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { | ||
| 56 | let this = self.get_mut(); | ||
| 57 | let mut mutex = this.inner.borrow_mut(); | ||
| 58 | mutex.with(|state| { | ||
| 59 | let serial = state.classes.get_serial(); | ||
| 60 | let serial = Pin::new(serial); | ||
| 61 | |||
| 62 | match serial.poll_fill_buf(cx) { | ||
| 63 | Poll::Ready(Ok(buf)) => { | ||
| 64 | let buf: &[u8] = buf; | ||
| 65 | // NOTE(unsafe) This part of the buffer won't be modified until the user calls | ||
| 66 | // consume, which will invalidate this ref | ||
| 67 | let buf: &[u8] = unsafe { core::mem::transmute(buf) }; | ||
| 68 | Poll::Ready(Ok(buf)) | ||
| 69 | } | ||
| 70 | Poll::Ready(Err(_)) => Poll::Ready(Err(io::Error::Other)), | ||
| 71 | Poll::Pending => Poll::Pending, | ||
| 72 | } | ||
| 73 | }) | ||
| 74 | } | ||
| 75 | |||
| 76 | fn consume(self: Pin<&mut Self>, amt: usize) { | ||
| 77 | let this = self.get_mut(); | ||
| 78 | let mut mutex = this.inner.borrow_mut(); | ||
| 79 | mutex.with(|state| { | ||
| 80 | let serial = state.classes.get_serial(); | ||
| 81 | let serial = Pin::new(serial); | ||
| 82 | |||
| 83 | serial.consume(amt); | ||
| 84 | }) | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | impl<'a, 'bus, 'c, I, B, T, INT> AsyncWrite for WriteInterface<'a, 'bus, 'c, I, B, T, INT> | ||
| 89 | where | ||
| 90 | I: Unpin, | ||
| 91 | B: UsbBus, | ||
| 92 | T: SerialState<'bus, 'c, B, I> + ClassSet<B>, | ||
| 93 | INT: USBInterrupt, | ||
| 94 | { | ||
| 95 | fn poll_write( | ||
| 96 | self: Pin<&mut Self>, | ||
| 97 | cx: &mut Context<'_>, | ||
| 98 | buf: &[u8], | ||
| 99 | ) -> Poll<io::Result<usize>> { | ||
| 100 | let this = self.get_mut(); | ||
| 101 | let mut mutex = this.inner.borrow_mut(); | ||
| 102 | mutex.with(|state| { | ||
| 103 | let serial = state.classes.get_serial(); | ||
| 104 | let serial = Pin::new(serial); | ||
| 105 | |||
| 106 | serial.poll_write(cx, buf) | ||
| 107 | }) | ||
| 108 | } | ||
| 109 | |||
| 110 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
| 111 | let this = self.get_mut(); | ||
| 112 | let mut mutex = this.inner.borrow_mut(); | ||
| 113 | mutex.with(|state| { | ||
| 114 | let serial = state.classes.get_serial(); | ||
| 115 | let serial = Pin::new(serial); | ||
| 116 | |||
| 117 | serial.poll_flush(cx) | ||
| 118 | }) | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | pub struct UsbSerial<'bus, 'a, B: UsbBus> { | ||
| 123 | inner: CdcAcmClass<'bus, B>, | ||
| 124 | read_buf: RingBuffer<'a>, | ||
| 125 | write_buf: RingBuffer<'a>, | ||
| 126 | read_waker: WakerRegistration, | ||
| 127 | write_waker: WakerRegistration, | ||
| 128 | write_state: WriteState, | ||
| 129 | read_error: bool, | ||
| 130 | write_error: bool, | ||
| 131 | } | ||
| 132 | |||
| 133 | impl<'bus, 'a, B: UsbBus> AsyncBufRead for UsbSerial<'bus, 'a, B> { | ||
| 134 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { | ||
| 135 | let this = self.get_mut(); | ||
| 136 | |||
| 137 | if this.read_error { | ||
| 138 | this.read_error = false; | ||
| 139 | return Poll::Ready(Err(io::Error::Other)); | ||
| 140 | } | ||
| 141 | |||
| 142 | let buf = this.read_buf.pop_buf(); | ||
| 143 | if buf.is_empty() { | ||
| 144 | this.read_waker.register(cx.waker()); | ||
| 145 | return Poll::Pending; | ||
| 146 | } | ||
| 147 | Poll::Ready(Ok(buf)) | ||
| 148 | } | ||
| 149 | |||
| 150 | fn consume(self: Pin<&mut Self>, amt: usize) { | ||
| 151 | self.get_mut().read_buf.pop(amt); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | impl<'bus, 'a, B: UsbBus> AsyncWrite for UsbSerial<'bus, 'a, B> { | ||
| 156 | fn poll_write( | ||
| 157 | self: Pin<&mut Self>, | ||
| 158 | cx: &mut Context<'_>, | ||
| 159 | buf: &[u8], | ||
| 160 | ) -> Poll<io::Result<usize>> { | ||
| 161 | let this = self.get_mut(); | ||
| 162 | |||
| 163 | if this.write_error { | ||
| 164 | this.write_error = false; | ||
| 165 | return Poll::Ready(Err(io::Error::Other)); | ||
| 166 | } | ||
| 167 | |||
| 168 | let write_buf = this.write_buf.push_buf(); | ||
| 169 | if write_buf.is_empty() { | ||
| 170 | trace!("buf full, registering waker"); | ||
| 171 | this.write_waker.register(cx.waker()); | ||
| 172 | return Poll::Pending; | ||
| 173 | } | ||
| 174 | |||
| 175 | let count = write_buf.len().min(buf.len()); | ||
| 176 | write_buf[..count].copy_from_slice(&buf[..count]); | ||
| 177 | this.write_buf.push(count); | ||
| 178 | |||
| 179 | this.flush_write(); | ||
| 180 | Poll::Ready(Ok(count)) | ||
| 181 | } | ||
| 182 | |||
| 183 | fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
| 184 | Poll::Ready(Ok(())) | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | /// Keeps track of the type of the last written packet. | ||
| 189 | enum WriteState { | ||
| 190 | /// No packets in-flight | ||
| 191 | Idle, | ||
| 192 | |||
| 193 | /// Short packet currently in-flight | ||
| 194 | Short, | ||
| 195 | |||
| 196 | /// Full packet current in-flight. A full packet must be followed by a short packet for the host | ||
| 197 | /// OS to see the transaction. The data is the number of subsequent full packets sent so far. A | ||
| 198 | /// short packet is forced every SHORT_PACKET_INTERVAL packets so that the OS sees data in a | ||
| 199 | /// timely manner. | ||
| 200 | Full(usize), | ||
| 201 | } | ||
| 202 | |||
| 203 | impl<'bus, 'a, B: UsbBus> UsbSerial<'bus, 'a, B> { | ||
| 204 | pub fn new( | ||
| 205 | alloc: &'bus UsbBusAllocator<B>, | ||
| 206 | read_buf: &'a mut [u8], | ||
| 207 | write_buf: &'a mut [u8], | ||
| 208 | ) -> Self { | ||
| 209 | Self { | ||
| 210 | inner: CdcAcmClass::new(alloc, 64), | ||
| 211 | read_buf: RingBuffer::new(read_buf), | ||
| 212 | write_buf: RingBuffer::new(write_buf), | ||
| 213 | read_waker: WakerRegistration::new(), | ||
| 214 | write_waker: WakerRegistration::new(), | ||
| 215 | write_state: WriteState::Idle, | ||
| 216 | read_error: false, | ||
| 217 | write_error: false, | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | fn flush_write(&mut self) { | ||
| 222 | /// If this many full size packets have been sent in a row, a short packet will be sent so that the | ||
| 223 | /// host sees the data in a timely manner. | ||
| 224 | const SHORT_PACKET_INTERVAL: usize = 10; | ||
| 225 | |||
| 226 | let full_size_packets = match self.write_state { | ||
| 227 | WriteState::Full(c) => c, | ||
| 228 | _ => 0, | ||
| 229 | }; | ||
| 230 | |||
| 231 | let ep_size = self.inner.max_packet_size() as usize; | ||
| 232 | let max_size = if full_size_packets > SHORT_PACKET_INTERVAL { | ||
| 233 | ep_size - 1 | ||
| 234 | } else { | ||
| 235 | ep_size | ||
| 236 | }; | ||
| 237 | |||
| 238 | let buf = { | ||
| 239 | let buf = self.write_buf.pop_buf(); | ||
| 240 | if buf.len() > max_size { | ||
| 241 | &buf[..max_size] | ||
| 242 | } else { | ||
| 243 | buf | ||
| 244 | } | ||
| 245 | }; | ||
| 246 | |||
| 247 | if !buf.is_empty() { | ||
| 248 | trace!("writing packet len {}", buf.len()); | ||
| 249 | let count = match self.inner.write_packet(buf) { | ||
| 250 | Ok(c) => { | ||
| 251 | trace!("write packet: OK {}", c); | ||
| 252 | c | ||
| 253 | } | ||
| 254 | Err(UsbError::WouldBlock) => { | ||
| 255 | trace!("write packet: WouldBlock"); | ||
| 256 | 0 | ||
| 257 | } | ||
| 258 | Err(_) => { | ||
| 259 | trace!("write packet: error"); | ||
| 260 | self.write_error = true; | ||
| 261 | return; | ||
| 262 | } | ||
| 263 | }; | ||
| 264 | |||
| 265 | if buf.len() == ep_size { | ||
| 266 | self.write_state = WriteState::Full(full_size_packets + 1); | ||
| 267 | } else { | ||
| 268 | self.write_state = WriteState::Short; | ||
| 269 | } | ||
| 270 | self.write_buf.pop(count); | ||
| 271 | } else if full_size_packets > 0 { | ||
| 272 | trace!("writing empty packet"); | ||
| 273 | match self.inner.write_packet(&[]) { | ||
| 274 | Ok(_) => { | ||
| 275 | trace!("write empty packet: OK"); | ||
| 276 | } | ||
| 277 | Err(UsbError::WouldBlock) => { | ||
| 278 | trace!("write empty packet: WouldBlock"); | ||
| 279 | return; | ||
| 280 | } | ||
| 281 | Err(_) => { | ||
| 282 | trace!("write empty packet: Error"); | ||
| 283 | self.write_error = true; | ||
| 284 | return; | ||
| 285 | } | ||
| 286 | } | ||
| 287 | self.write_state = WriteState::Idle; | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | impl<B> UsbClass<B> for UsbSerial<'_, '_, B> | ||
| 293 | where | ||
| 294 | B: UsbBus, | ||
| 295 | { | ||
| 296 | fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<(), UsbError> { | ||
| 297 | self.inner.get_configuration_descriptors(writer) | ||
| 298 | } | ||
| 299 | |||
| 300 | fn reset(&mut self) { | ||
| 301 | self.inner.reset(); | ||
| 302 | self.read_buf.clear(); | ||
| 303 | self.write_buf.clear(); | ||
| 304 | self.write_state = WriteState::Idle; | ||
| 305 | self.read_waker.wake(); | ||
| 306 | self.write_waker.wake(); | ||
| 307 | } | ||
| 308 | |||
| 309 | fn endpoint_in_complete(&mut self, addr: EndpointAddress) { | ||
| 310 | trace!("DONE endpoint_in_complete"); | ||
| 311 | if addr == self.inner.write_ep_address() { | ||
| 312 | trace!("DONE writing packet, waking"); | ||
| 313 | self.write_waker.wake(); | ||
| 314 | |||
| 315 | self.flush_write(); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | fn endpoint_out(&mut self, addr: EndpointAddress) { | ||
| 320 | if addr == self.inner.read_ep_address() { | ||
| 321 | let buf = self.read_buf.push_buf(); | ||
| 322 | let count = match self.inner.read_packet(buf) { | ||
| 323 | Ok(c) => c, | ||
| 324 | Err(UsbError::WouldBlock) => 0, | ||
| 325 | Err(_) => { | ||
| 326 | self.read_error = true; | ||
| 327 | return; | ||
| 328 | } | ||
| 329 | }; | ||
| 330 | |||
| 331 | if count > 0 { | ||
| 332 | self.read_buf.push(count); | ||
| 333 | self.read_waker.wake(); | ||
| 334 | } | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | fn control_in(&mut self, xfer: ControlIn<B>) { | ||
| 339 | self.inner.control_in(xfer); | ||
| 340 | } | ||
| 341 | |||
| 342 | fn control_out(&mut self, xfer: ControlOut<B>) { | ||
| 343 | self.inner.control_out(xfer); | ||
| 344 | } | ||
| 345 | } | ||
diff --git a/embassy-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 = [] | |||
| 31 | pool-64 = [] | 31 | pool-64 = [] |
| 32 | pool-128 = [] | 32 | pool-128 = [] |
| 33 | 33 | ||
| 34 | nightly = ["embedded-io/async"] | ||
| 35 | |||
| 34 | [dependencies] | 36 | [dependencies] |
| 35 | 37 | ||
| 36 | defmt = { version = "0.3", optional = true } | 38 | defmt = { version = "0.3", optional = true } |
| 37 | log = { version = "0.4.14", optional = true } | 39 | log = { version = "0.4.14", optional = true } |
| 38 | 40 | ||
| 39 | embassy = { version = "0.1.0", path = "../embassy" } | 41 | embassy = { version = "0.1.0", path = "../embassy" } |
| 42 | embedded-io = "0.2.0" | ||
| 40 | 43 | ||
| 41 | managed = { version = "0.8.0", default-features = false, features = [ "map" ] } | 44 | managed = { version = "0.8.0", default-features = false, features = [ "map" ] } |
| 42 | heapless = { version = "0.7.5", default-features = false } | 45 | heapless = { 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; | |||
| 4 | use smoltcp::time::Instant as SmolInstant; | 4 | use smoltcp::time::Instant as SmolInstant; |
| 5 | 5 | ||
| 6 | use crate::packet_pool::PacketBoxExt; | 6 | use crate::packet_pool::PacketBoxExt; |
| 7 | use crate::Result; | ||
| 8 | use crate::{Packet, PacketBox, PacketBuf}; | 7 | use 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 | ||
| 80 | impl smoltcp::phy::RxToken for RxToken { | 79 | impl 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 | ||
| 94 | impl<'a> smoltcp::phy::TxToken for TxToken<'a> { | 93 | impl<'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. |
| 5 | pub(crate) mod fmt; | 9 | pub(crate) mod fmt; |
| @@ -20,9 +24,7 @@ pub use stack::{ | |||
| 20 | }; | 24 | }; |
| 21 | 25 | ||
| 22 | #[cfg(feature = "tcp")] | 26 | #[cfg(feature = "tcp")] |
| 23 | mod tcp_socket; | 27 | pub mod tcp; |
| 24 | #[cfg(feature = "tcp")] | ||
| 25 | pub use tcp_socket::TcpSocket; | ||
| 26 | 28 | ||
| 27 | // smoltcp reexports | 29 | // smoltcp reexports |
| 28 | pub use smoltcp::phy::{DeviceCapabilities, Medium}; | 30 | pub use smoltcp::phy::{DeviceCapabilities, Medium}; |
| @@ -32,4 +34,3 @@ pub use smoltcp::time::Instant as SmolInstant; | |||
| 32 | pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; | 34 | pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; |
| 33 | pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; | 35 | pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; |
| 34 | pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>; | 36 | pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>; |
| 35 | pub 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 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::task::Poll; | ||
| 3 | use futures::future::poll_fn; | ||
| 4 | |||
| 5 | use super::{Error, TcpSocket}; | ||
| 6 | |||
| 7 | impl<'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 | |||
| 35 | impl<'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 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use core::mem; | 2 | use core::mem; |
| 3 | use core::pin::Pin; | 3 | use core::task::Poll; |
| 4 | use core::task::{Context, Poll}; | ||
| 5 | use embassy::io; | ||
| 6 | use embassy::io::{AsyncBufRead, AsyncWrite}; | ||
| 7 | use smoltcp::iface::{Context as SmolContext, SocketHandle}; | 4 | use smoltcp::iface::{Context as SmolContext, SocketHandle}; |
| 8 | use smoltcp::socket::TcpSocket as SyncTcpSocket; | 5 | use smoltcp::socket::TcpSocket as SyncTcpSocket; |
| 9 | use smoltcp::socket::{TcpSocketBuffer, TcpState}; | 6 | use smoltcp::socket::{TcpSocketBuffer, TcpState}; |
| 10 | use smoltcp::time::Duration; | 7 | use smoltcp::time::Duration; |
| 11 | use smoltcp::wire::IpEndpoint; | 8 | use smoltcp::wire::IpEndpoint; |
| 12 | 9 | ||
| 10 | #[cfg(feature = "nightly")] | ||
| 11 | mod io_impl; | ||
| 12 | |||
| 13 | use super::stack::Stack; | 13 | use super::stack::Stack; |
| 14 | use crate::{Error, Result}; | 14 | |
| 15 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||
| 16 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 17 | pub enum Error { | ||
| 18 | ConnectionReset, | ||
| 19 | } | ||
| 20 | |||
| 21 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | ||
| 22 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 23 | pub 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))] | ||
| 36 | pub 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 | ||
| 16 | pub struct TcpSocket<'a> { | 45 | pub 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 | ||
| 133 | fn to_ioerr(_err: Error) -> io::Error { | ||
| 134 | // todo | ||
| 135 | io::Error::Other | ||
| 136 | } | ||
| 137 | |||
| 138 | impl<'a> Drop for TcpSocket<'a> { | 176 | impl<'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 | ||
| 146 | impl<'a> AsyncBufRead for TcpSocket<'a> { | 184 | impl 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 | ||
| 183 | impl<'a> AsyncWrite for TcpSocket<'a> { | 190 | impl<'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 | ||
| 20 | time = ["embassy/time"] | 20 | time = ["embassy/time"] |
| 21 | 21 | ||
| 22 | defmt = ["dep:defmt", "embassy/defmt", "embassy-usb?/defmt"] | 22 | defmt = ["dep:defmt", "embassy/defmt", "embassy-usb?/defmt", "embedded-io?/defmt"] |
| 23 | 23 | ||
| 24 | # Enable nightly-only features | 24 | # Enable nightly-only features |
| 25 | nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async"] | 25 | nightly = ["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 } | |||
| 73 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 73 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 74 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} | 74 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} |
| 75 | embedded-hal-async = { version = "0.1.0-alpha.0", optional = true} | 75 | embedded-hal-async = { version = "0.1.0-alpha.0", optional = true} |
| 76 | embedded-io = { version = "0.2.0", features = ["async"], optional = true } | ||
| 76 | 77 | ||
| 77 | defmt = { version = "0.3", optional = true } | 78 | defmt = { version = "0.3", optional = true } |
| 78 | log = { version = "0.4.14", optional = true } | 79 | log = { 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 | ||
| 16 | use core::cmp::min; | 16 | use core::cmp::min; |
| 17 | use core::future::Future; | ||
| 17 | use core::marker::PhantomData; | 18 | use core::marker::PhantomData; |
| 18 | use core::mem; | ||
| 19 | use core::pin::Pin; | ||
| 20 | use core::sync::atomic::{compiler_fence, Ordering}; | 19 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 21 | use core::task::{Context, Poll}; | 20 | use core::task::Poll; |
| 22 | use embassy::interrupt::InterruptExt; | 21 | use embassy::interrupt::InterruptExt; |
| 23 | use embassy::io::{AsyncBufRead, AsyncWrite}; | ||
| 24 | use embassy::util::Unborrow; | 22 | use embassy::util::Unborrow; |
| 25 | use embassy::waitqueue::WakerRegistration; | 23 | use embassy::waitqueue::WakerRegistration; |
| 26 | use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; | 24 | use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; |
| 27 | use embassy_hal_common::ring_buffer::RingBuffer; | 25 | use embassy_hal_common::ring_buffer::RingBuffer; |
| 28 | use embassy_hal_common::{low_power_wait_until, unborrow}; | 26 | use embassy_hal_common::{low_power_wait_until, unborrow}; |
| 27 | use futures::future::poll_fn; | ||
| 29 | 28 | ||
| 30 | use crate::gpio::Pin as GpioPin; | 29 | use crate::gpio::Pin as GpioPin; |
| 31 | use crate::pac; | 30 | use crate::pac; |
| @@ -197,82 +196,99 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { | |||
| 197 | } | 196 | } |
| 198 | } | 197 | } |
| 199 | 198 | ||
| 200 | impl<'d, U: UarteInstance, T: TimerInstance> AsyncBufRead for BufferedUarte<'d, U, T> { | 199 | impl<'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]>> { | 203 | impl<'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 | ||
| 233 | impl<'d, U: UarteInstance, T: TimerInstance> AsyncWrite for BufferedUarte<'d, U, T> { | 239 | impl<'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")] |
| 65 | mod time_driver; | 65 | mod time_driver; |
| 66 | 66 | ||
| 67 | #[cfg(feature = "nightly")] | ||
| 67 | pub mod buffered_uarte; | 68 | pub mod buffered_uarte; |
| 68 | pub mod gpio; | 69 | pub 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 | |||
| 53 | rand_core = "0.6.3" | 53 | rand_core = "0.6.3" |
| 54 | sdio-host = "0.5.0" | 54 | sdio-host = "0.5.0" |
| 55 | embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true } | 55 | embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true } |
| 56 | synopsys-usb-otg = { version = "0.3", features = ["cortex-m", "hs"], optional = true } | ||
| 57 | critical-section = "0.2.5" | 56 | critical-section = "0.2.5" |
| 58 | bare-metal = "1.0.0" | 57 | bare-metal = "1.0.0" |
| 59 | atomic-polyfill = "0.1.5" | 58 | atomic-polyfill = "0.1.5" |
| 60 | stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } | 59 | stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } |
| 61 | vcell = { version = "0.1.3", optional = true } | 60 | vcell = "0.1.3" |
| 62 | bxcan = "0.6.2" | 61 | bxcan = "0.6.2" |
| 63 | nb = "1.0.0" | 62 | nb = "1.0.0" |
| 64 | stm32-fmc = "0.2.4" | 63 | stm32-fmc = "0.2.4" |
| 65 | seq-macro = "0.2.2" | 64 | seq-macro = "0.2.2" |
| 66 | cfg-if = "1.0.0" | 65 | cfg-if = "1.0.0" |
| 66 | embedded-io = { version = "0.2.0", features = ["async"], optional = true } | ||
| 67 | 67 | ||
| 68 | [build-dependencies] | 68 | [build-dependencies] |
| 69 | proc-macro2 = "1.0.36" | 69 | proc-macro2 = "1.0.36" |
| @@ -71,12 +71,12 @@ quote = "1.0.15" | |||
| 71 | stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} | 71 | stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} |
| 72 | 72 | ||
| 73 | [features] | 73 | [features] |
| 74 | defmt = ["dep:defmt", "embassy/defmt", "embedded-io?/defmt" ] | ||
| 74 | sdmmc-rs = ["embedded-sdmmc"] | 75 | sdmmc-rs = ["embedded-sdmmc"] |
| 75 | net = ["embassy-net", "vcell"] | 76 | net = ["embassy-net" ] |
| 76 | memory-x = ["stm32-metapac/memory-x"] | 77 | memory-x = ["stm32-metapac/memory-x"] |
| 77 | subghz = [] | 78 | subghz = [] |
| 78 | exti = [] | 79 | exti = [] |
| 79 | usb-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"] | |||
| 90 | time-driver-tim15 = ["_time-driver"] | 90 | time-driver-tim15 = ["_time-driver"] |
| 91 | 91 | ||
| 92 | # Enable nightly-only features | 92 | # Enable nightly-only features |
| 93 | nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async"] | 93 | nightly = ["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")] |
| 5 | mod _version; | 6 | mod _version; |
| 6 | pub mod generic_smi; | 7 | pub mod generic_smi; |
| 7 | 8 | ||
| 9 | #[cfg(feature = "net")] | ||
| 8 | pub use _version::*; | 10 | pub 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 @@ | |||
| 1 | use core::convert::TryInto; | ||
| 2 | use core::ptr::write_volatile; | ||
| 3 | |||
| 4 | use crate::flash::Error; | ||
| 5 | use crate::pac; | ||
| 6 | |||
| 7 | pub(crate) unsafe fn lock() { | ||
| 8 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | ||
| 9 | } | ||
| 10 | |||
| 11 | pub(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 | |||
| 16 | pub(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 | |||
| 42 | pub(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 | |||
| 74 | pub(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 | |||
| 88 | pub(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 @@ | |||
| 1 | use core::convert::TryInto; | ||
| 2 | use core::ptr::write_volatile; | ||
| 3 | |||
| 4 | use atomic_polyfill::{fence, Ordering}; | ||
| 5 | |||
| 6 | use crate::flash::Error; | ||
| 7 | use crate::pac; | ||
| 8 | |||
| 9 | pub(crate) unsafe fn lock() { | ||
| 10 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | ||
| 11 | } | ||
| 12 | |||
| 13 | pub(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 | |||
| 18 | pub(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 | |||
| 52 | pub(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 | |||
| 75 | unsafe 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 | |||
| 94 | pub(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 | |||
| 114 | pub(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 @@ | |||
| 1 | use core::convert::TryInto; | ||
| 2 | use core::ptr::write_volatile; | ||
| 3 | |||
| 4 | use crate::flash::Error; | ||
| 5 | use crate::pac; | ||
| 6 | |||
| 7 | const SECOND_BANK_OFFSET: usize = 0x0010_0000; | ||
| 8 | |||
| 9 | const fn is_dual_bank() -> bool { | ||
| 10 | super::FLASH_SIZE / 2 > super::ERASE_SIZE | ||
| 11 | } | ||
| 12 | |||
| 13 | pub(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 | |||
| 20 | pub(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 | |||
| 30 | pub(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 | |||
| 74 | pub(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 | |||
| 104 | unsafe 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 | |||
| 123 | pub(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 | |||
| 128 | unsafe 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 | |||
| 164 | pub(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 @@ | |||
| 1 | use core::convert::TryInto; | ||
| 2 | use core::ptr::write_volatile; | ||
| 3 | |||
| 4 | use crate::flash::Error; | ||
| 5 | use crate::pac; | ||
| 6 | |||
| 7 | pub(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 | |||
| 19 | pub(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 | |||
| 36 | pub(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 | |||
| 66 | pub(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 | |||
| 112 | pub(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 | |||
| 150 | pub(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 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::peripherals::FLASH; | 1 | use crate::peripherals::FLASH; |
| 3 | use core::convert::TryInto; | ||
| 4 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 5 | use core::ptr::write_volatile; | ||
| 6 | use embassy::util::Unborrow; | 3 | use embassy::util::Unborrow; |
| 7 | use embassy_hal_common::unborrow; | 4 | use embassy_hal_common::unborrow; |
| 8 | 5 | ||
| @@ -17,6 +14,12 @@ pub use crate::pac::FLASH_SIZE; | |||
| 17 | pub use crate::pac::WRITE_SIZE; | 14 | pub use crate::pac::WRITE_SIZE; |
| 18 | const FLASH_END: usize = FLASH_BASE + FLASH_SIZE; | 15 | const 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")] | ||
| 21 | mod family; | ||
| 22 | |||
| 20 | pub struct Flash<'d> { | 23 | pub 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 | ||
| 279 | impl<'d> ErrorType for Flash<'d> { | 112 | impl<'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; | |||
| 39 | pub mod dac; | 39 | pub mod dac; |
| 40 | #[cfg(dcmi)] | 40 | #[cfg(dcmi)] |
| 41 | pub mod dcmi; | 41 | pub mod dcmi; |
| 42 | #[cfg(all(eth, feature = "net"))] | 42 | #[cfg(eth)] |
| 43 | pub mod eth; | 43 | pub mod eth; |
| 44 | #[cfg(feature = "exti")] | 44 | #[cfg(feature = "exti")] |
| 45 | pub mod exti; | 45 | pub mod exti; |
| @@ -50,7 +50,9 @@ pub mod i2c; | |||
| 50 | 50 | ||
| 51 | #[cfg(crc)] | 51 | #[cfg(crc)] |
| 52 | pub mod crc; | 52 | pub 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 | ))] | ||
| 54 | pub mod flash; | 56 | pub mod flash; |
| 55 | pub mod pwm; | 57 | pub mod pwm; |
| 56 | #[cfg(rng)] | 58 | #[cfg(rng)] |
| @@ -61,7 +63,7 @@ pub mod sdmmc; | |||
| 61 | pub mod spi; | 63 | pub mod spi; |
| 62 | #[cfg(usart)] | 64 | #[cfg(usart)] |
| 63 | pub mod usart; | 65 | pub mod usart; |
| 64 | #[cfg(feature = "usb-otg")] | 66 | #[cfg(any(otgfs, otghs))] |
| 65 | pub mod usb_otg; | 67 | pub 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 @@ | |||
| 1 | use atomic_polyfill::{compiler_fence, Ordering}; | ||
| 2 | use core::future::Future; | ||
| 3 | use core::task::Poll; | ||
| 4 | use embassy::waitqueue::WakerRegistration; | ||
| 5 | use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; | ||
| 6 | use embassy_hal_common::ring_buffer::RingBuffer; | ||
| 7 | use futures::future::poll_fn; | ||
| 8 | |||
| 9 | use super::*; | ||
| 10 | |||
| 11 | pub struct State<'d, T: Instance>(StateStorage<StateInner<'d, T>>); | ||
| 12 | impl<'d, T: Instance> State<'d, T> { | ||
| 13 | pub fn new() -> Self { | ||
| 14 | Self(StateStorage::new()) | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | struct 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 | |||
| 28 | unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} | ||
| 29 | unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} | ||
| 30 | |||
| 31 | pub struct BufferedUart<'d, T: Instance> { | ||
| 32 | inner: PeripheralMutex<'d, StateInner<'d, T>>, | ||
| 33 | } | ||
| 34 | |||
| 35 | impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} | ||
| 36 | |||
| 37 | impl<'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 | |||
| 66 | impl<'d, T: Instance> StateInner<'d, T> | ||
| 67 | where | ||
| 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 | |||
| 135 | impl<'d, T: Instance> PeripheralState for StateInner<'d, T> | ||
| 136 | where | ||
| 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 | |||
| 146 | impl embedded_io::Error for Error { | ||
| 147 | fn kind(&self) -> embedded_io::ErrorKind { | ||
| 148 | embedded_io::ErrorKind::Other | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { | ||
| 153 | type Error = Error; | ||
| 154 | } | ||
| 155 | |||
| 156 | impl<'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 | |||
| 194 | impl<'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")] | ||
| 427 | pub use buffered::*; | 428 | pub use buffered::*; |
| 428 | mod buffered { | 429 | #[cfg(feature = "nightly")] |
| 429 | use atomic_polyfill::{compiler_fence, Ordering}; | 430 | mod 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)] |
| 650 | fn tdr(r: crate::pac::usart::Usart) -> *mut u8 { | 433 | fn 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)] | ||
| 665 | unsafe fn clear_interrupt_flags(_r: crate::pac::usart::Usart, _sr: regs::Sr) { | 449 | unsafe 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)] | ||
| 685 | unsafe fn clear_interrupt_flags(r: crate::pac::usart::Usart, sr: regs::Ixr) { | 470 | unsafe 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 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use embassy::util::Unborrow; | 2 | use embassy::util::Unborrow; |
| 3 | use embassy_hal_common::unborrow; | 3 | use embassy_hal_common::unborrow; |
| 4 | use synopsys_usb_otg::{PhyType, UsbPeripheral}; | ||
| 5 | 4 | ||
| 6 | use crate::gpio::sealed::AFType; | 5 | use crate::gpio::sealed::AFType; |
| 7 | use crate::gpio::Speed; | 6 | use crate::gpio::Speed; |
| 8 | use crate::{peripherals, rcc::RccPeripheral}; | 7 | use crate::{peripherals, rcc::RccPeripheral}; |
| 9 | 8 | ||
| 10 | pub use embassy_hal_common::usb::*; | ||
| 11 | pub use synopsys_usb_otg::UsbBus; | ||
| 12 | |||
| 13 | macro_rules! config_ulpi_pins { | 9 | macro_rules! config_ulpi_pins { |
| 14 | ($($pin:ident),*) => { | 10 | ($($pin:ident),*) => { |
| 15 | unborrow!($($pin),*); | 11 | unborrow!($($pin),*); |
| @@ -23,9 +19,24 @@ macro_rules! config_ulpi_pins { | |||
| 23 | }; | 19 | }; |
| 24 | } | 20 | } |
| 25 | 21 | ||
| 22 | /// USB PHY type | ||
| 23 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
| 24 | pub enum PhyType { | ||
| 25 | /// Internal Full-Speed PHY | ||
| 26 | /// | ||
| 27 | /// Available on most High-Speed peripherals. | ||
| 28 | InternalFullSpeed, | ||
| 29 | /// Internal High-Speed PHY | ||
| 30 | /// | ||
| 31 | /// Available on a few STM32 chips. | ||
| 32 | InternalHighSpeed, | ||
| 33 | /// External ULPI High-Speed PHY | ||
| 34 | ExternalHighSpeed, | ||
| 35 | } | ||
| 36 | |||
| 26 | pub struct UsbOtg<'d, T: Instance> { | 37 | pub struct UsbOtg<'d, T: Instance> { |
| 27 | phantom: PhantomData<&'d mut T>, | 38 | phantom: PhantomData<&'d mut T>, |
| 28 | phy_type: PhyType, | 39 | _phy_type: PhyType, |
| 29 | } | 40 | } |
| 30 | 41 | ||
| 31 | impl<'d, T: Instance> UsbOtg<'d, T> { | 42 | impl<'d, T: Instance> UsbOtg<'d, T> { |
| @@ -44,7 +55,7 @@ impl<'d, T: Instance> UsbOtg<'d, T> { | |||
| 44 | 55 | ||
| 45 | Self { | 56 | Self { |
| 46 | phantom: PhantomData, | 57 | phantom: PhantomData, |
| 47 | phy_type: PhyType::InternalFullSpeed, | 58 | _phy_type: PhyType::InternalFullSpeed, |
| 48 | } | 59 | } |
| 49 | } | 60 | } |
| 50 | 61 | ||
| @@ -71,7 +82,7 @@ impl<'d, T: Instance> UsbOtg<'d, T> { | |||
| 71 | 82 | ||
| 72 | Self { | 83 | Self { |
| 73 | phantom: PhantomData, | 84 | phantom: PhantomData, |
| 74 | phy_type: PhyType::ExternalHighSpeed, | 85 | _phy_type: PhyType::ExternalHighSpeed, |
| 75 | } | 86 | } |
| 76 | } | 87 | } |
| 77 | } | 88 | } |
| @@ -83,29 +94,6 @@ impl<'d, T: Instance> Drop for UsbOtg<'d, T> { | |||
| 83 | } | 94 | } |
| 84 | } | 95 | } |
| 85 | 96 | ||
| 86 | unsafe impl<'d, T: Instance> Send for UsbOtg<'d, T> {} | ||
| 87 | unsafe impl<'d, T: Instance> Sync for UsbOtg<'d, T> {} | ||
| 88 | |||
| 89 | unsafe impl<'d, T: Instance> UsbPeripheral for UsbOtg<'d, T> { | ||
| 90 | const REGISTERS: *const () = T::REGISTERS; | ||
| 91 | const HIGH_SPEED: bool = T::HIGH_SPEED; | ||
| 92 | const FIFO_DEPTH_WORDS: usize = T::FIFO_DEPTH_WORDS; | ||
| 93 | const ENDPOINT_COUNT: usize = T::ENDPOINT_COUNT; | ||
| 94 | |||
| 95 | fn enable() { | ||
| 96 | <T as crate::rcc::sealed::RccPeripheral>::enable(); | ||
| 97 | <T as crate::rcc::sealed::RccPeripheral>::reset(); | ||
| 98 | } | ||
| 99 | |||
| 100 | fn phy_type(&self) -> PhyType { | ||
| 101 | self.phy_type | ||
| 102 | } | ||
| 103 | |||
| 104 | fn ahb_frequency_hz(&self) -> u32 { | ||
| 105 | <T as crate::rcc::sealed::RccPeripheral>::frequency().0 | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | pub(crate) mod sealed { | 97 | pub(crate) mod sealed { |
| 110 | pub trait Instance { | 98 | pub trait Instance { |
| 111 | const REGISTERS: *const (); | 99 | const REGISTERS: *const (); |
| @@ -177,7 +165,7 @@ foreach_peripheral!( | |||
| 177 | const FIFO_DEPTH_WORDS: usize = 512; | 165 | const FIFO_DEPTH_WORDS: usize = 512; |
| 178 | const ENDPOINT_COUNT: usize = 8; | 166 | const ENDPOINT_COUNT: usize = 8; |
| 179 | } else { | 167 | } else { |
| 180 | compile_error!("USB_OTG_FS peripheral is not supported by this chip. Disable \"usb-otg-fs\" feature or select a different chip."); | 168 | compile_error!("USB_OTG_FS peripheral is not supported by this chip."); |
| 181 | } | 169 | } |
| 182 | } | 170 | } |
| 183 | } | 171 | } |
| @@ -214,7 +202,7 @@ foreach_peripheral!( | |||
| 214 | const FIFO_DEPTH_WORDS: usize = 1024; | 202 | const FIFO_DEPTH_WORDS: usize = 1024; |
| 215 | const ENDPOINT_COUNT: usize = 9; | 203 | const ENDPOINT_COUNT: usize = 9; |
| 216 | } else { | 204 | } else { |
| 217 | compile_error!("USB_OTG_HS peripheral is not supported by this chip. Disable \"usb-otg-hs\" feature or select a different chip."); | 205 | compile_error!("USB_OTG_HS peripheral is not supported by this chip."); |
| 218 | } | 206 | } |
| 219 | } | 207 | } |
| 220 | } | 208 | } |
| @@ -222,12 +210,3 @@ foreach_peripheral!( | |||
| 222 | impl Instance for peripherals::$inst {} | 210 | impl Instance for peripherals::$inst {} |
| 223 | }; | 211 | }; |
| 224 | ); | 212 | ); |
| 225 | |||
| 226 | foreach_interrupt!( | ||
| 227 | ($inst:ident, otgfs, $block:ident, GLOBAL, $irq:ident) => { | ||
| 228 | unsafe impl USBInterrupt for crate::interrupt::$irq {} | ||
| 229 | }; | ||
| 230 | ($inst:ident, otghs, $block:ident, GLOBAL, $irq:ident) => { | ||
| 231 | unsafe impl USBInterrupt for crate::interrupt::$irq {} | ||
| 232 | }; | ||
| 233 | ); | ||
diff --git a/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] | ||
| 5 | pub 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 | |||
| 79 | pub type Result<T> = core::result::Result<T, Error>; | ||
| 80 | |||
| 81 | #[cfg(feature = "std")] | ||
| 82 | impl 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")] | ||
| 108 | impl 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 | |||
| 135 | impl 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")] | ||
| 142 | impl 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 @@ | |||
| 1 | mod error; | ||
| 2 | #[cfg(feature = "std")] | ||
| 3 | mod std; | ||
| 4 | mod traits; | ||
| 5 | mod util; | ||
| 6 | |||
| 7 | pub use self::error::*; | ||
| 8 | #[cfg(feature = "std")] | ||
| 9 | pub use self::std::*; | ||
| 10 | pub use self::traits::*; | ||
| 11 | pub 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 @@ | |||
| 1 | use core::pin::Pin; | ||
| 2 | use core::task::{Context, Poll}; | ||
| 3 | use futures::io as std_io; | ||
| 4 | |||
| 5 | use super::{AsyncBufRead, AsyncWrite, Result}; | ||
| 6 | |||
| 7 | pub struct FromStdIo<T>(T); | ||
| 8 | |||
| 9 | impl<T> FromStdIo<T> { | ||
| 10 | pub fn new(inner: T) -> Self { | ||
| 11 | Self(inner) | ||
| 12 | } | ||
| 13 | } | ||
| 14 | |||
| 15 | impl<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 | |||
| 28 | impl<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 @@ | |||
| 1 | use core::ops::DerefMut; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::task::{Context, Poll}; | ||
| 4 | |||
| 5 | #[cfg(feature = "alloc")] | ||
| 6 | use alloc::boxed::Box; | ||
| 7 | |||
| 8 | use 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. | ||
| 17 | pub 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. | ||
| 72 | pub 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 | |||
| 103 | macro_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")] | ||
| 116 | impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for Box<T> { | ||
| 117 | defer_async_read!(); | ||
| 118 | } | ||
| 119 | |||
| 120 | impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for &mut T { | ||
| 121 | defer_async_read!(); | ||
| 122 | } | ||
| 123 | |||
| 124 | impl<P> AsyncBufRead for Pin<P> | ||
| 125 | where | ||
| 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 | |||
| 138 | macro_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")] | ||
| 155 | impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> { | ||
| 156 | deref_async_write!(); | ||
| 157 | } | ||
| 158 | |||
| 159 | impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for &mut T { | ||
| 160 | deref_async_write!(); | ||
| 161 | } | ||
| 162 | |||
| 163 | impl<P> AsyncWrite for Pin<P> | ||
| 164 | where | ||
| 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 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::task::{Context, Poll}; | ||
| 4 | use futures::ready; | ||
| 5 | use pin_project::pin_project; | ||
| 6 | |||
| 7 | use 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 | /// ``` | ||
| 34 | pub fn copy_buf<R, W>(reader: R, writer: &mut W) -> CopyBuf<'_, R, W> | ||
| 35 | where | ||
| 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"] | ||
| 50 | pub struct CopyBuf<'a, R, W: ?Sized> { | ||
| 51 | #[pin] | ||
| 52 | reader: R, | ||
| 53 | writer: &'a mut W, | ||
| 54 | amt: usize, | ||
| 55 | } | ||
| 56 | |||
| 57 | impl<R, W> Future for CopyBuf<'_, R, W> | ||
| 58 | where | ||
| 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 @@ | |||
| 1 | use core::pin::Pin; | ||
| 2 | use futures::future::Future; | ||
| 3 | use futures::task::{Context, Poll}; | ||
| 4 | |||
| 5 | use super::super::error::Result; | ||
| 6 | use super::super::traits::AsyncBufRead; | ||
| 7 | |||
| 8 | pub struct Drain<'a, R: ?Sized> { | ||
| 9 | reader: &'a mut R, | ||
| 10 | } | ||
| 11 | |||
| 12 | impl<R: ?Sized + Unpin> Unpin for Drain<'_, R> {} | ||
| 13 | |||
| 14 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> Drain<'a, R> { | ||
| 15 | pub(super) fn new(reader: &'a mut R) -> Self { | ||
| 16 | Self { reader } | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<'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 @@ | |||
| 1 | use core::pin::Pin; | ||
| 2 | use futures::future::Future; | ||
| 3 | use futures::ready; | ||
| 4 | use futures::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use super::super::error::Result; | ||
| 7 | use 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"] | ||
| 12 | pub struct Flush<'a, W: ?Sized> { | ||
| 13 | writer: &'a mut W, | ||
| 14 | } | ||
| 15 | |||
| 16 | impl<W: ?Sized + Unpin> Unpin for Flush<'_, W> {} | ||
| 17 | |||
| 18 | impl<'a, W: AsyncWrite + ?Sized + Unpin> Flush<'a, W> { | ||
| 19 | pub(super) fn new(writer: &'a mut W) -> Self { | ||
| 20 | Flush { writer } | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | impl<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 @@ | |||
| 1 | use core::cmp::min; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::task::{Context, Poll}; | ||
| 4 | use futures::ready; | ||
| 5 | |||
| 6 | mod read; | ||
| 7 | pub use self::read::Read; | ||
| 8 | |||
| 9 | mod read_buf; | ||
| 10 | pub use self::read_buf::ReadBuf; | ||
| 11 | |||
| 12 | mod read_byte; | ||
| 13 | pub use self::read_byte::ReadByte; | ||
| 14 | |||
| 15 | mod read_exact; | ||
| 16 | pub use self::read_exact::ReadExact; | ||
| 17 | |||
| 18 | mod read_while; | ||
| 19 | pub use self::read_while::ReadWhile; | ||
| 20 | |||
| 21 | mod read_to_end; | ||
| 22 | pub use self::read_to_end::ReadToEnd; | ||
| 23 | |||
| 24 | mod skip_while; | ||
| 25 | pub use self::skip_while::SkipWhile; | ||
| 26 | |||
| 27 | mod drain; | ||
| 28 | pub use self::drain::Drain; | ||
| 29 | |||
| 30 | mod flush; | ||
| 31 | pub use self::flush::Flush; | ||
| 32 | |||
| 33 | mod write; | ||
| 34 | pub use self::write::Write; | ||
| 35 | |||
| 36 | mod write_all; | ||
| 37 | pub use self::write_all::WriteAll; | ||
| 38 | |||
| 39 | mod write_byte; | ||
| 40 | pub use self::write_byte::WriteByte; | ||
| 41 | |||
| 42 | #[cfg(feature = "alloc")] | ||
| 43 | mod split; | ||
| 44 | #[cfg(feature = "alloc")] | ||
| 45 | pub use self::split::{split, ReadHalf, WriteHalf}; | ||
| 46 | |||
| 47 | mod copy_buf; | ||
| 48 | pub use self::copy_buf::{copy_buf, CopyBuf}; | ||
| 49 | |||
| 50 | use super::error::Result; | ||
| 51 | use super::traits::{AsyncBufRead, AsyncWrite}; | ||
| 52 | |||
| 53 | pub 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 | |||
| 131 | impl<R: AsyncBufRead + ?Sized> AsyncBufReadExt for R {} | ||
| 132 | |||
| 133 | pub 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 | |||
| 145 | pub 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 | |||
| 177 | impl<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 @@ | |||
| 1 | use super::super::error::Result; | ||
| 2 | use super::super::traits::AsyncBufRead; | ||
| 3 | |||
| 4 | use core::cmp::min; | ||
| 5 | |||
| 6 | use core::pin::Pin; | ||
| 7 | use futures::future::Future; | ||
| 8 | use futures::ready; | ||
| 9 | use 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"] | ||
| 14 | pub struct Read<'a, R: ?Sized> { | ||
| 15 | reader: &'a mut R, | ||
| 16 | buf: &'a mut [u8], | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<R: ?Sized + Unpin> Unpin for Read<'_, R> {} | ||
| 20 | |||
| 21 | impl<'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 | |||
| 27 | impl<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 @@ | |||
| 1 | use super::super::error::Result; | ||
| 2 | use super::super::traits::AsyncBufRead; | ||
| 3 | |||
| 4 | use core::pin::Pin; | ||
| 5 | use futures::future::Future; | ||
| 6 | use futures::ready; | ||
| 7 | use futures::task::{Context, Poll}; | ||
| 8 | |||
| 9 | pub struct ReadBuf<'a, R: ?Sized> { | ||
| 10 | reader: Option<&'a mut R>, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl<R: ?Sized + Unpin> Unpin for ReadBuf<'_, R> {} | ||
| 14 | |||
| 15 | impl<'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 | |||
| 23 | impl<'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 @@ | |||
| 1 | use core::pin::Pin; | ||
| 2 | use futures::future::Future; | ||
| 3 | use futures::ready; | ||
| 4 | use futures::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use super::super::error::{Error, Result}; | ||
| 7 | use super::super::traits::AsyncBufRead; | ||
| 8 | |||
| 9 | pub struct ReadByte<'a, R: ?Sized> { | ||
| 10 | reader: &'a mut R, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl<R: ?Sized + Unpin> Unpin for ReadByte<'_, R> {} | ||
| 14 | |||
| 15 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadByte<'a, R> { | ||
| 16 | pub(super) fn new(reader: &'a mut R) -> Self { | ||
| 17 | Self { reader } | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | impl<'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 @@ | |||
| 1 | use super::super::error::{Error, Result}; | ||
| 2 | use super::super::traits::AsyncBufRead; | ||
| 3 | |||
| 4 | use core::cmp::min; | ||
| 5 | use core::mem; | ||
| 6 | use core::pin::Pin; | ||
| 7 | use futures::future::Future; | ||
| 8 | use futures::ready; | ||
| 9 | use 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"] | ||
| 14 | pub struct ReadExact<'a, R: ?Sized> { | ||
| 15 | reader: &'a mut R, | ||
| 16 | buf: &'a mut [u8], | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<R: ?Sized + Unpin> Unpin for ReadExact<'_, R> {} | ||
| 20 | |||
| 21 | impl<'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 | |||
| 27 | impl<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 @@ | |||
| 1 | use core::cmp::min; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use futures::future::Future; | ||
| 4 | use futures::ready; | ||
| 5 | use futures::task::{Context, Poll}; | ||
| 6 | |||
| 7 | use super::super::error::{Error, Result}; | ||
| 8 | use super::super::traits::AsyncBufRead; | ||
| 9 | |||
| 10 | pub struct ReadToEnd<'a, R: ?Sized> { | ||
| 11 | reader: &'a mut R, | ||
| 12 | buf: &'a mut [u8], | ||
| 13 | n: usize, | ||
| 14 | } | ||
| 15 | |||
| 16 | impl<R: ?Sized + Unpin> Unpin for ReadToEnd<'_, R> {} | ||
| 17 | |||
| 18 | impl<'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 | |||
| 24 | impl<'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 @@ | |||
| 1 | use core::cmp::min; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use futures::future::Future; | ||
| 4 | use futures::ready; | ||
| 5 | use futures::task::{Context, Poll}; | ||
| 6 | |||
| 7 | use super::super::error::{Error, Result}; | ||
| 8 | use super::super::traits::AsyncBufRead; | ||
| 9 | |||
| 10 | pub struct ReadWhile<'a, R: ?Sized, F> { | ||
| 11 | reader: &'a mut R, | ||
| 12 | buf: &'a mut [u8], | ||
| 13 | n: usize, | ||
| 14 | f: F, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl<R: ?Sized + Unpin, F> Unpin for ReadWhile<'_, R, F> {} | ||
| 18 | |||
| 19 | impl<'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 | |||
| 30 | impl<'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 @@ | |||
| 1 | use core::iter::Iterator; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use futures::future::Future; | ||
| 4 | use futures::ready; | ||
| 5 | use futures::task::{Context, Poll}; | ||
| 6 | |||
| 7 | use super::super::error::{Error, Result}; | ||
| 8 | use super::super::traits::AsyncBufRead; | ||
| 9 | |||
| 10 | pub struct SkipWhile<'a, R: ?Sized, F> { | ||
| 11 | reader: &'a mut R, | ||
| 12 | f: F, | ||
| 13 | } | ||
| 14 | |||
| 15 | impl<R: ?Sized + Unpin, F> Unpin for SkipWhile<'_, R, F> {} | ||
| 16 | |||
| 17 | impl<'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 | |||
| 23 | impl<'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 @@ | |||
| 1 | use alloc::rc::Rc; | ||
| 2 | use core::cell::UnsafeCell; | ||
| 3 | use core::pin::Pin; | ||
| 4 | use futures::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use super::super::error::Result; | ||
| 7 | use super::super::traits::{AsyncBufRead, AsyncWrite}; | ||
| 8 | |||
| 9 | /// The readable half of an object returned from `AsyncBufRead::split`. | ||
| 10 | #[derive(Debug)] | ||
| 11 | pub struct ReadHalf<T> { | ||
| 12 | handle: Rc<UnsafeCell<T>>, | ||
| 13 | } | ||
| 14 | |||
| 15 | /// The writable half of an object returned from `AsyncBufRead::split`. | ||
| 16 | #[derive(Debug)] | ||
| 17 | pub struct WriteHalf<T> { | ||
| 18 | handle: Rc<UnsafeCell<T>>, | ||
| 19 | } | ||
| 20 | |||
| 21 | impl<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 | |||
| 31 | impl<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 | |||
| 40 | pub 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 @@ | |||
| 1 | use core::pin::Pin; | ||
| 2 | use futures::future::Future; | ||
| 3 | use futures::ready; | ||
| 4 | use futures::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use super::super::error::Result; | ||
| 7 | use 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"] | ||
| 12 | pub struct Write<'a, W: ?Sized> { | ||
| 13 | writer: &'a mut W, | ||
| 14 | buf: &'a [u8], | ||
| 15 | } | ||
| 16 | |||
| 17 | impl<W: ?Sized + Unpin> Unpin for Write<'_, W> {} | ||
| 18 | |||
| 19 | impl<'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 | |||
| 25 | impl<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 @@ | |||
| 1 | use core::mem; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use futures::future::Future; | ||
| 4 | use futures::ready; | ||
| 5 | use futures::task::{Context, Poll}; | ||
| 6 | |||
| 7 | use super::super::error::Result; | ||
| 8 | use 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"] | ||
| 13 | pub struct WriteAll<'a, W: ?Sized> { | ||
| 14 | writer: &'a mut W, | ||
| 15 | buf: &'a [u8], | ||
| 16 | } | ||
| 17 | |||
| 18 | impl<W: ?Sized + Unpin> Unpin for WriteAll<'_, W> {} | ||
| 19 | |||
| 20 | impl<'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 | |||
| 26 | impl<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 @@ | |||
| 1 | use core::pin::Pin; | ||
| 2 | use futures::future::Future; | ||
| 3 | use futures::ready; | ||
| 4 | use futures::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use super::super::error::Result; | ||
| 7 | use 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"] | ||
| 12 | pub struct WriteByte<'a, W: ?Sized> { | ||
| 13 | writer: &'a mut W, | ||
| 14 | byte: u8, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl<W: ?Sized + Unpin> Unpin for WriteByte<'_, W> {} | ||
| 18 | |||
| 19 | impl<'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 | |||
| 25 | impl<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; | |||
| 13 | pub mod executor; | 13 | pub mod executor; |
| 14 | #[cfg(cortex_m)] | 14 | #[cfg(cortex_m)] |
| 15 | pub mod interrupt; | 15 | pub mod interrupt; |
| 16 | pub mod io; | ||
| 17 | pub mod mutex; | 16 | pub mod mutex; |
| 18 | #[cfg(feature = "time")] | 17 | #[cfg(feature = "time")] |
| 19 | pub mod time; | 18 | pub 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` | ||
| 3 | runner = "probe-run --chip STM32F303VCTx" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "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] | ||
| 2 | authors = ["Ulf Lilleengen <[email protected]>"] | ||
| 3 | edition = "2021" | ||
| 4 | name = "embassy-boot-stm32f3-examples" | ||
| 5 | version = "0.1.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] } | ||
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } | ||
| 10 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" } | ||
| 11 | embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" } | ||
| 12 | |||
| 13 | defmt = { version = "0.3", optional = true } | ||
| 14 | defmt-rtt = { version = "0.3", optional = true } | ||
| 15 | panic-reset = { version = "0.1.1" } | ||
| 16 | embedded-hal = { version = "0.2.6" } | ||
| 17 | |||
| 18 | cortex-m = "0.7.3" | ||
| 19 | cortex-m-rt = "0.7.0" | ||
| 20 | |||
| 21 | [features] | ||
| 22 | defmt = [ | ||
| 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 | |||
| 3 | Example for STM32F3 demonstrating the bootloader. The example consists of application binaries, 'a' | ||
| 4 | which allows you to press a button to start the DFU process, and 'b' which is the updated | ||
| 5 | application. | ||
| 6 | |||
| 7 | |||
| 8 | ## Prerequisites | ||
| 9 | |||
| 10 | * `cargo-binutils` | ||
| 11 | * `cargo-flash` | ||
| 12 | * `embassy-boot-stm32` | ||
| 13 | |||
| 14 | ## Usage | ||
| 15 | |||
| 16 | ``` | ||
| 17 | # Flash bootloader | ||
| 18 | cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f303re --chip STM32F303RETx | ||
| 19 | # Build 'b' | ||
| 20 | cargo build --release --bin b | ||
| 21 | # Generate binary for 'b' | ||
| 22 | cargo objcopy --release --bin b -- -O binary b.bin | ||
| 23 | ``` | ||
| 24 | |||
| 25 | # Flash `a` (which includes b.bin) | ||
| 26 | |||
| 27 | ``` | ||
| 28 | cargo 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 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn 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 @@ | |||
| 1 | MEMORY | ||
| 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 | |||
| 5 | use embassy_boot_stm32::FirmwareUpdater; | ||
| 6 | use embassy_stm32::exti::ExtiInput; | ||
| 7 | use embassy_stm32::flash::Flash; | ||
| 8 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | ||
| 9 | use embassy_stm32::Peripherals; | ||
| 10 | use embassy_traits::adapter::BlockingAsync; | ||
| 11 | use panic_reset as _; | ||
| 12 | |||
| 13 | #[cfg(feature = "defmt-rtt")] | ||
| 14 | use defmt_rtt::*; | ||
| 15 | |||
| 16 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | ||
| 17 | |||
| 18 | #[embassy::main] | ||
| 19 | async 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 | |||
| 5 | use embassy::executor::Spawner; | ||
| 6 | use embassy::time::{Duration, Timer}; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_stm32::Peripherals; | ||
| 9 | use panic_reset as _; | ||
| 10 | |||
| 11 | #[cfg(feature = "defmt-rtt")] | ||
| 12 | use defmt_rtt::*; | ||
| 13 | |||
| 14 | #[embassy::main] | ||
| 15 | async 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` | ||
| 3 | runner = "probe-run --chip STM32F767ZITx -v" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "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] | ||
| 2 | authors = ["Ulf Lilleengen <[email protected]>"] | ||
| 3 | edition = "2021" | ||
| 4 | name = "embassy-boot-stm32f7-examples" | ||
| 5 | version = "0.1.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] } | ||
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } | ||
| 10 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" } | ||
| 11 | embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" } | ||
| 12 | |||
| 13 | defmt = { version = "0.3", optional = true } | ||
| 14 | defmt-rtt = { version = "0.3", optional = true } | ||
| 15 | panic-reset = { version = "0.1.1" } | ||
| 16 | embedded-hal = { version = "0.2.6" } | ||
| 17 | |||
| 18 | cortex-m = "0.7.3" | ||
| 19 | cortex-m-rt = "0.7.0" | ||
| 20 | |||
| 21 | [features] | ||
| 22 | defmt = [ | ||
| 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 | |||
| 3 | Example for STM32F7 demonstrating the bootloader. The example consists of application binaries, 'a' | ||
| 4 | which allows you to press a button to start the DFU process, and 'b' which is the updated | ||
| 5 | application. | ||
| 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' | ||
| 20 | cargo build --release --bin b | ||
| 21 | # Generate binary for 'b' | ||
| 22 | cargo objcopy --release --bin b -- -O binary b.bin | ||
| 23 | ``` | ||
| 24 | |||
| 25 | # Flash `a` (which includes b.bin) | ||
| 26 | |||
| 27 | ``` | ||
| 28 | cargo 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 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn 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 | ||
| 2 | mv ../../../embassy-boot/stm32/memory.x ../../../embassy-boot/stm32/memory-old.x | ||
| 3 | cp memory-bl.x ../../../embassy-boot/stm32/memory.x | ||
| 4 | |||
| 5 | cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32F767ZITx --target thumbv7em-none-eabihf | ||
| 6 | |||
| 7 | rm ../../../embassy-boot/stm32/memory.x | ||
| 8 | mv ../../../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 @@ | |||
| 1 | MEMORY | ||
| 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 @@ | |||
| 1 | MEMORY | ||
| 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 | |||
| 5 | use embassy_boot_stm32::FirmwareUpdater; | ||
| 6 | use embassy_stm32::exti::ExtiInput; | ||
| 7 | use embassy_stm32::flash::Flash; | ||
| 8 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | ||
| 9 | use embassy_stm32::Peripherals; | ||
| 10 | use embassy_traits::adapter::BlockingAsync; | ||
| 11 | use panic_reset as _; | ||
| 12 | |||
| 13 | #[cfg(feature = "defmt-rtt")] | ||
| 14 | use defmt_rtt::*; | ||
| 15 | |||
| 16 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | ||
| 17 | |||
| 18 | #[embassy::main] | ||
| 19 | async 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 | |||
| 5 | use embassy::executor::Spawner; | ||
| 6 | use embassy::time::{Duration, Timer}; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_stm32::Peripherals; | ||
| 9 | use panic_reset as _; | ||
| 10 | |||
| 11 | #[cfg(feature = "defmt-rtt")] | ||
| 12 | use defmt_rtt::*; | ||
| 13 | |||
| 14 | #[embassy::main] | ||
| 15 | async 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` | ||
| 3 | runner = "probe-run --chip STM32H743ZITx" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "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] | ||
| 2 | authors = ["Ulf Lilleengen <[email protected]>"] | ||
| 3 | edition = "2021" | ||
| 4 | name = "embassy-boot-stm32f7-examples" | ||
| 5 | version = "0.1.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] } | ||
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } | ||
| 10 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" } | ||
| 11 | embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" } | ||
| 12 | |||
| 13 | defmt = { version = "0.3", optional = true } | ||
| 14 | defmt-rtt = { version = "0.3", optional = true } | ||
| 15 | panic-reset = { version = "0.1.1" } | ||
| 16 | embedded-hal = { version = "0.2.6" } | ||
| 17 | |||
| 18 | cortex-m = "0.7.3" | ||
| 19 | cortex-m-rt = "0.7.0" | ||
| 20 | |||
| 21 | [features] | ||
| 22 | defmt = [ | ||
| 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 | |||
| 3 | Example for STM32H7 demonstrating the bootloader. The example consists of application binaries, 'a' | ||
| 4 | which allows you to press a button to start the DFU process, and 'b' which is the updated | ||
| 5 | application. | ||
| 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' | ||
| 20 | cargo build --release --bin b | ||
| 21 | # Generate binary for 'b' | ||
| 22 | cargo objcopy --release --bin b -- -O binary b.bin | ||
| 23 | ``` | ||
| 24 | |||
| 25 | # Flash `a` (which includes b.bin) | ||
| 26 | |||
| 27 | ``` | ||
| 28 | cargo 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 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn 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 | ||
| 2 | mv ../../../embassy-boot/stm32/memory.x ../../../embassy-boot/stm32/memory-old.x | ||
| 3 | cp memory-bl.x ../../../embassy-boot/stm32/memory.x | ||
| 4 | |||
| 5 | cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32H743ZITx --target thumbv7em-none-eabihf | ||
| 6 | |||
| 7 | rm ../../../embassy-boot/stm32/memory.x | ||
| 8 | mv ../../../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 @@ | |||
| 1 | MEMORY | ||
| 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 @@ | |||
| 1 | MEMORY | ||
| 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 | |||
| 5 | use embassy_boot_stm32::FirmwareUpdater; | ||
| 6 | use embassy_stm32::exti::ExtiInput; | ||
| 7 | use embassy_stm32::flash::Flash; | ||
| 8 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | ||
| 9 | use embassy_stm32::Peripherals; | ||
| 10 | use embassy_traits::adapter::BlockingAsync; | ||
| 11 | use panic_reset as _; | ||
| 12 | |||
| 13 | #[cfg(feature = "defmt-rtt")] | ||
| 14 | use defmt_rtt::*; | ||
| 15 | |||
| 16 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | ||
| 17 | |||
| 18 | #[embassy::main] | ||
| 19 | async 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 | |||
| 5 | use embassy::executor::Spawner; | ||
| 6 | use embassy::time::{Duration, Timer}; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_stm32::Peripherals; | ||
| 9 | use panic_reset as _; | ||
| 10 | |||
| 11 | #[cfg(feature = "defmt-rtt")] | ||
| 12 | use defmt_rtt::*; | ||
| 13 | |||
| 14 | #[embassy::main] | ||
| 15 | async 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] |
| 8 | default = ["nightly"] | 8 | default = ["nightly"] |
| 9 | nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm"] | 9 | nightly = ["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] |
| 12 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy = { 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 | |||
| 16 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } | 16 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } |
| 17 | embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } | 17 | embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } |
| 18 | embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true } | 18 | embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true } |
| 19 | embedded-io = "0.2.0" | ||
| 19 | 20 | ||
| 20 | defmt = "0.3" | 21 | defmt = "0.3" |
| 21 | defmt-rtt = "0.3" | 22 | defmt-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 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy::executor::Spawner; | 6 | use embassy::executor::Spawner; |
| 7 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 8 | use embassy_nrf::buffered_uarte::State; | 7 | use embassy_nrf::buffered_uarte::State; |
| 9 | use embassy_nrf::{buffered_uarte::BufferedUarte, interrupt, uarte, Peripherals}; | 8 | use embassy_nrf::{buffered_uarte::BufferedUarte, interrupt, uarte, Peripherals}; |
| 9 | use embedded_io::asynch::{Read, Write}; | ||
| 10 | use futures::pin_mut; | 10 | use futures::pin_mut; |
| 11 | 11 | ||
| 12 | use defmt_rtt as _; // global logger | 12 | use 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::*; | |||
| 10 | use embassy::blocking_mutex::raw::ThreadModeRawMutex; | 10 | use embassy::blocking_mutex::raw::ThreadModeRawMutex; |
| 11 | use embassy::channel::Channel; | 11 | use embassy::channel::Channel; |
| 12 | use embassy::executor::Spawner; | 12 | use embassy::executor::Spawner; |
| 13 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 14 | use embassy::util::Forever; | 13 | use embassy::util::Forever; |
| 15 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, TcpSocket}; | 14 | use embassy_net::tcp::TcpSocket; |
| 15 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf}; | ||
| 16 | use embassy_nrf::pac; | 16 | use embassy_nrf::pac; |
| 17 | use embassy_nrf::usb::Driver; | 17 | use embassy_nrf::usb::Driver; |
| 18 | use embassy_nrf::Peripherals; | 18 | use embassy_nrf::Peripherals; |
| @@ -20,7 +20,9 @@ use embassy_nrf::{interrupt, peripherals}; | |||
| 20 | use embassy_usb::{Builder, Config, UsbDevice}; | 20 | use embassy_usb::{Builder, Config, UsbDevice}; |
| 21 | use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; | 21 | use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; |
| 22 | 22 | ||
| 23 | use defmt_rtt as _; // global logger | 23 | use defmt_rtt as _; |
| 24 | use embedded_io::asynch::{Read, Write}; | ||
| 25 | // global logger | ||
| 24 | use panic_probe as _; | 26 | use panic_probe as _; |
| 25 | 27 | ||
| 26 | type MyDriver = Driver<'static, peripherals::USBD>; | 28 | type 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] |
| 8 | embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] } | 8 | embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] } |
| 9 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] } | 9 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["nightly", "std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] } |
| 10 | embedded-io = { version = "0.2.0", features = ["async", "std"] } | ||
| 10 | 11 | ||
| 11 | async-io = "1.6.0" | 12 | async-io = "1.6.0" |
| 12 | env_logger = "0.9.0" | 13 | env_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 | ||
| 3 | use clap::Parser; | 3 | use clap::Parser; |
| 4 | use embassy::executor::{Executor, Spawner}; | 4 | use embassy::executor::{Executor, Spawner}; |
| 5 | use embassy::io::AsyncWriteExt; | ||
| 6 | use embassy::util::Forever; | 5 | use embassy::util::Forever; |
| 6 | use embassy_net::tcp::TcpSocket; | ||
| 7 | use embassy_net::{ | 7 | use embassy_net::{ |
| 8 | Config, Configurator, DhcpConfigurator, Ipv4Address, Ipv4Cidr, StackResources, | 8 | Config, Configurator, DhcpConfigurator, Ipv4Address, Ipv4Cidr, StackResources, |
| 9 | StaticConfigurator, TcpSocket, | 9 | StaticConfigurator, |
| 10 | }; | 10 | }; |
| 11 | use embedded_io::asynch::Write; | ||
| 11 | use heapless::Vec; | 12 | use heapless::Vec; |
| 12 | use log::*; | 13 | use 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 | ||
| 6 | use async_io::Async; | 6 | use async_io::Async; |
| 7 | use embassy::executor::Executor; | 7 | use embassy::executor::Executor; |
| 8 | use embassy::io::AsyncBufReadExt; | ||
| 9 | use embassy::util::Forever; | 8 | use embassy::util::Forever; |
| 9 | use embedded_io::asynch::Read; | ||
| 10 | use log::*; | 10 | use log::*; |
| 11 | use nix::sys::termios; | 11 | use 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"] } | |||
| 19 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 19 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 20 | heapless = { version = "0.7.5", default-features = false } | 20 | heapless = { version = "0.7.5", default-features = false } |
| 21 | nb = "1.0.0" | 21 | nb = "1.0.0" |
| 22 | embedded-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 _; | |||
| 15 | async fn main(_spawner: Spawner, p: Peripherals) { | 15 | async 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 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy_stm32::flash::Flash; | ||
| 8 | use embassy_stm32::Peripherals; | ||
| 9 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 10 | |||
| 11 | use defmt_rtt as _; // global logger | ||
| 12 | use panic_probe as _; | ||
| 13 | |||
| 14 | #[embassy::main] | ||
| 15 | async 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] |
| 10 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } | 10 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "usb-otg"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 14 | defmt-rtt = "0.3" |
diff --git a/examples/stm32f4/src/bin/usb_uart.rs b/examples/stm32f4/src/bin/usb_uart.rs deleted file mode 100644 index 251ed1eb0..000000000 --- a/examples/stm32f4/src/bin/usb_uart.rs +++ /dev/null | |||
| @@ -1,99 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt_rtt as _; // global logger | ||
| 6 | use panic_probe as _; | ||
| 7 | |||
| 8 | use defmt::{info, unwrap}; | ||
| 9 | use defmt_rtt as _; // global logger | ||
| 10 | use embassy::interrupt::InterruptExt; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use panic_probe as _; // print out panic messages | ||
| 13 | |||
| 14 | use embassy::executor::Spawner; | ||
| 15 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 16 | use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial}; | ||
| 17 | use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals}; | ||
| 18 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 19 | |||
| 20 | static mut EP_MEMORY: [u32; 2048] = [0; 2048]; | ||
| 21 | |||
| 22 | // USB requires at least 48 MHz clock | ||
| 23 | fn config() -> Config { | ||
| 24 | let mut config = Config::default(); | ||
| 25 | config.rcc.sys_ck = Some(Hertz(48_000_000)); | ||
| 26 | config | ||
| 27 | } | ||
| 28 | |||
| 29 | #[embassy::main(config = "config()")] | ||
| 30 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 31 | let mut rx_buffer = [0u8; 64]; | ||
| 32 | // we send back input + cr + lf | ||
| 33 | let mut tx_buffer = [0u8; 66]; | ||
| 34 | |||
| 35 | let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11); | ||
| 36 | let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY }); | ||
| 37 | |||
| 38 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 39 | |||
| 40 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 41 | .manufacturer("Fake company") | ||
| 42 | .product("Serial port") | ||
| 43 | .serial_number("TEST") | ||
| 44 | .device_class(0x02) | ||
| 45 | .build(); | ||
| 46 | |||
| 47 | let irq = interrupt::take!(OTG_FS); | ||
| 48 | irq.set_priority(interrupt::Priority::P3); | ||
| 49 | |||
| 50 | let mut state = State::new(); | ||
| 51 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 52 | pin_mut!(usb); | ||
| 53 | |||
| 54 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 55 | |||
| 56 | info!("usb initialized!"); | ||
| 57 | |||
| 58 | unwrap!( | ||
| 59 | writer | ||
| 60 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 61 | .await | ||
| 62 | ); | ||
| 63 | |||
| 64 | let mut buf = [0u8; 64]; | ||
| 65 | loop { | ||
| 66 | let mut n = 0; | ||
| 67 | |||
| 68 | async { | ||
| 69 | loop { | ||
| 70 | let char = unwrap!(reader.read_byte().await); | ||
| 71 | |||
| 72 | if char == b'\r' || char == b'\n' { | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | |||
| 76 | buf[n] = char; | ||
| 77 | n += 1; | ||
| 78 | |||
| 79 | // stop if we're out of room | ||
| 80 | if n == buf.len() { | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | .await; | ||
| 86 | |||
| 87 | if n > 0 { | ||
| 88 | for char in buf[..n].iter_mut() { | ||
| 89 | // upper case | ||
| 90 | if 0x61 <= *char && *char <= 0x7a { | ||
| 91 | *char &= !0x20; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 95 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 96 | unwrap!(writer.flush().await); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
diff --git a/examples/stm32f4/src/bin/usb_uart_ulpi.rs b/examples/stm32f4/src/bin/usb_uart_ulpi.rs deleted file mode 100644 index f6c373602..000000000 --- a/examples/stm32f4/src/bin/usb_uart_ulpi.rs +++ /dev/null | |||
| @@ -1,114 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt_rtt as _; // global logger | ||
| 6 | use panic_probe as _; | ||
| 7 | |||
| 8 | use defmt::{info, unwrap}; | ||
| 9 | use defmt_rtt as _; // global logger | ||
| 10 | use embassy::interrupt::InterruptExt; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use panic_probe as _; // print out panic messages | ||
| 13 | |||
| 14 | use embassy::executor::Spawner; | ||
| 15 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 16 | use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial}; | ||
| 17 | use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals}; | ||
| 18 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 19 | |||
| 20 | static mut EP_MEMORY: [u32; 2048] = [0; 2048]; | ||
| 21 | |||
| 22 | // USB requires at least 48 MHz clock | ||
| 23 | fn config() -> Config { | ||
| 24 | let mut config = Config::default(); | ||
| 25 | config.rcc.sys_ck = Some(Hertz(48_000_000)); | ||
| 26 | config | ||
| 27 | } | ||
| 28 | |||
| 29 | #[embassy::main(config = "config()")] | ||
| 30 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 31 | let mut rx_buffer = [0u8; 64]; | ||
| 32 | // we send back input + cr + lf | ||
| 33 | let mut tx_buffer = [0u8; 66]; | ||
| 34 | |||
| 35 | // USB with external high-speed PHY | ||
| 36 | let peri = UsbOtg::new_hs_ulpi( | ||
| 37 | p.USB_OTG_HS, | ||
| 38 | p.PA5, | ||
| 39 | p.PC2, | ||
| 40 | p.PC3, | ||
| 41 | p.PC0, | ||
| 42 | p.PA3, | ||
| 43 | p.PB0, | ||
| 44 | p.PB1, | ||
| 45 | p.PB10, | ||
| 46 | p.PB11, | ||
| 47 | p.PB12, | ||
| 48 | p.PB13, | ||
| 49 | p.PB5, | ||
| 50 | ); | ||
| 51 | let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY }); | ||
| 52 | |||
| 53 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 54 | |||
| 55 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 56 | .manufacturer("Fake company") | ||
| 57 | .product("Serial port") | ||
| 58 | .serial_number("TEST") | ||
| 59 | .device_class(0x02) | ||
| 60 | .build(); | ||
| 61 | |||
| 62 | let irq = interrupt::take!(OTG_FS); | ||
| 63 | irq.set_priority(interrupt::Priority::P3); | ||
| 64 | |||
| 65 | let mut state = State::new(); | ||
| 66 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 67 | pin_mut!(usb); | ||
| 68 | |||
| 69 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 70 | |||
| 71 | info!("usb initialized!"); | ||
| 72 | |||
| 73 | unwrap!( | ||
| 74 | writer | ||
| 75 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 76 | .await | ||
| 77 | ); | ||
| 78 | |||
| 79 | let mut buf = [0u8; 64]; | ||
| 80 | loop { | ||
| 81 | let mut n = 0; | ||
| 82 | |||
| 83 | async { | ||
| 84 | loop { | ||
| 85 | let char = unwrap!(reader.read_byte().await); | ||
| 86 | |||
| 87 | if char == b'\r' || char == b'\n' { | ||
| 88 | break; | ||
| 89 | } | ||
| 90 | |||
| 91 | buf[n] = char; | ||
| 92 | n += 1; | ||
| 93 | |||
| 94 | // stop if we're out of room | ||
| 95 | if n == buf.len() { | ||
| 96 | break; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | .await; | ||
| 101 | |||
| 102 | if n > 0 { | ||
| 103 | for char in buf[..n].iter_mut() { | ||
| 104 | // upper case | ||
| 105 | if 0x61 <= *char && *char <= 0x7a { | ||
| 106 | *char &= !0x20; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 110 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 111 | unwrap!(writer.flush().await); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
diff --git a/examples/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" | |||
| 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } | 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } |
| 11 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } | 11 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } |
| 12 | embedded-io = { version = "0.2.0", features = ["async"] } | ||
| 12 | 13 | ||
| 13 | defmt = "0.3" | 14 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 15 | defmt-rtt = "0.3" |
| @@ -22,6 +23,7 @@ heapless = { version = "0.7.5", default-features = false } | |||
| 22 | nb = "1.0.0" | 23 | nb = "1.0.0" |
| 23 | rand_core = "0.6.3" | 24 | rand_core = "0.6.3" |
| 24 | critical-section = "0.2.3" | 25 | critical-section = "0.2.3" |
| 26 | embedded-storage = "0.3.0" | ||
| 25 | 27 | ||
| 26 | [dependencies.smoltcp] | 28 | [dependencies.smoltcp] |
| 27 | version = "0.8.0" | 29 | version = "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 @@ | |||
| 5 | use cortex_m_rt::entry; | 5 | use cortex_m_rt::entry; |
| 6 | use defmt::*; | 6 | use defmt::*; |
| 7 | use embassy::executor::{Executor, Spawner}; | 7 | use embassy::executor::{Executor, Spawner}; |
| 8 | use embassy::io::AsyncWriteExt; | ||
| 9 | use embassy::time::{Duration, Timer}; | 8 | use embassy::time::{Duration, Timer}; |
| 10 | use embassy::util::Forever; | 9 | use embassy::util::Forever; |
| 11 | use embassy_net::{ | 10 | use embassy_net::tcp::TcpSocket; |
| 12 | Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator, TcpSocket, | 11 | use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator}; |
| 13 | }; | ||
| 14 | use embassy_stm32::eth::generic_smi::GenericSMI; | 12 | use embassy_stm32::eth::generic_smi::GenericSMI; |
| 15 | use embassy_stm32::eth::{Ethernet, State}; | 13 | use embassy_stm32::eth::{Ethernet, State}; |
| 16 | use embassy_stm32::interrupt; | 14 | use embassy_stm32::interrupt; |
| @@ -19,6 +17,7 @@ use embassy_stm32::peripherals::RNG; | |||
| 19 | use embassy_stm32::rng::Rng; | 17 | use embassy_stm32::rng::Rng; |
| 20 | use embassy_stm32::time::U32Ext; | 18 | use embassy_stm32::time::U32Ext; |
| 21 | use embassy_stm32::Config; | 19 | use embassy_stm32::Config; |
| 20 | use embedded_io::asynch::Write; | ||
| 22 | use heapless::Vec; | 21 | use heapless::Vec; |
| 23 | 22 | ||
| 24 | use defmt_rtt as _; // global logger | 23 | use 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 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy::time::{Duration, Timer}; | ||
| 8 | use embassy_stm32::flash::Flash; | ||
| 9 | use embassy_stm32::Peripherals; | ||
| 10 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 11 | |||
| 12 | use defmt_rtt as _; // global logger | ||
| 13 | use panic_probe as _; | ||
| 14 | |||
| 15 | #[embassy::main] | ||
| 16 | async 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" | |||
| 5 | version = "0.1.0" | 5 | version = "0.1.0" |
| 6 | resolver = "2" | 6 | resolver = "2" |
| 7 | 7 | ||
| 8 | [features] | ||
| 9 | |||
| 10 | [dependencies] | 8 | [dependencies] |
| 11 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } | 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } |
| 12 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } | 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } |
| 13 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } | 11 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } |
| 12 | embedded-io = { version = "0.2.0", features = ["async"] } | ||
| 14 | 13 | ||
| 15 | defmt = "0.3" | 14 | defmt = "0.3" |
| 16 | defmt-rtt = "0.3" | 15 | defmt-rtt = "0.3" |
| @@ -27,6 +26,7 @@ rand_core = "0.6.3" | |||
| 27 | critical-section = "0.2.5" | 26 | critical-section = "0.2.5" |
| 28 | micromath = "2.0.0" | 27 | micromath = "2.0.0" |
| 29 | stm32-fmc = "0.2.4" | 28 | stm32-fmc = "0.2.4" |
| 29 | embedded-storage = "0.3.0" | ||
| 30 | 30 | ||
| 31 | [dependencies.smoltcp] | 31 | [dependencies.smoltcp] |
| 32 | version = "0.8.0" | 32 | version = "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 _; | |||
| 8 | use cortex_m_rt::entry; | 8 | use cortex_m_rt::entry; |
| 9 | use defmt::*; | 9 | use defmt::*; |
| 10 | use embassy::executor::{Executor, Spawner}; | 10 | use embassy::executor::{Executor, Spawner}; |
| 11 | use embassy::io::AsyncWriteExt; | ||
| 12 | use embassy::time::{Duration, Timer}; | 11 | use embassy::time::{Duration, Timer}; |
| 13 | use embassy::util::Forever; | 12 | use embassy::util::Forever; |
| 14 | use embassy_net::{ | 13 | use embassy_net::tcp::TcpSocket; |
| 15 | Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator, TcpSocket, | 14 | use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator}; |
| 16 | }; | ||
| 17 | use embassy_stm32::eth::generic_smi::GenericSMI; | 15 | use embassy_stm32::eth::generic_smi::GenericSMI; |
| 18 | use embassy_stm32::eth::{Ethernet, State}; | 16 | use embassy_stm32::eth::{Ethernet, State}; |
| 19 | use embassy_stm32::interrupt; | 17 | use embassy_stm32::interrupt; |
| @@ -22,6 +20,7 @@ use embassy_stm32::peripherals::RNG; | |||
| 22 | use embassy_stm32::rng::Rng; | 20 | use embassy_stm32::rng::Rng; |
| 23 | use embassy_stm32::time::U32Ext; | 21 | use embassy_stm32::time::U32Ext; |
| 24 | use embassy_stm32::Config; | 22 | use embassy_stm32::Config; |
| 23 | use embedded_io::asynch::Write; | ||
| 25 | use heapless::Vec; | 24 | use 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 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use defmt_rtt as _; // global logger | ||
| 7 | use embassy::executor::Spawner; | ||
| 8 | use embassy::time::{Duration, Timer}; | ||
| 9 | use embassy_stm32::flash::Flash; | ||
| 10 | use embassy_stm32::Peripherals; | ||
| 11 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 12 | use panic_probe as _; | ||
| 13 | |||
| 14 | #[embassy::main] | ||
| 15 | async 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` |
| 3 | runner = "probe-run --chip STM32L072CZTx" | 3 | runner = "probe-run --chip STM32L053R8Tx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv6m-none-eabi" | 6 | target = "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] |
| 9 | default = ["nightly"] | 9 | default = ["nightly"] |
| 10 | nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan"] | 10 | nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", "embedded-io/async"] |
| 11 | 11 | ||
| 12 | [dependencies] | 12 | [dependencies] |
| 13 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | 13 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } |
| 14 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } | 14 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } |
| 15 | |||
| 16 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} | 15 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} |
| 17 | 16 | ||
| 18 | lorawan-device = { version = "0.7.1", default-features = false, features = ["async"], optional = true } | 17 | lorawan-device = { version = "0.7.1", default-features = false, features = ["async"], optional = true } |
| @@ -22,6 +21,7 @@ defmt = "0.3" | |||
| 22 | defmt-rtt = "0.3" | 21 | defmt-rtt = "0.3" |
| 23 | 22 | ||
| 24 | embedded-storage = "0.3.0" | 23 | embedded-storage = "0.3.0" |
| 24 | embedded-io = "0.2.0" | ||
| 25 | 25 | ||
| 26 | cortex-m = "0.7.3" | 26 | cortex-m = "0.7.3" |
| 27 | cortex-m-rt = "0.7.0" | 27 | cortex-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 | ||
| 5 | use defmt_rtt as _; // global logger | 5 | use defmt_rtt as _; |
| 6 | use embedded_io::asynch::{Read, Write}; | ||
| 7 | // global logger | ||
| 6 | use panic_probe as _; | 8 | use panic_probe as _; |
| 7 | 9 | ||
| 8 | use defmt::*; | 10 | use defmt::*; |
| 9 | 11 | ||
| 10 | use embassy::executor::Spawner; | 12 | use embassy::executor::Spawner; |
| 11 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 12 | use embassy_stm32::dma::NoDma; | 13 | use embassy_stm32::dma::NoDma; |
| 13 | use embassy_stm32::interrupt; | 14 | use embassy_stm32::interrupt; |
| 14 | use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; | 15 | use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; |
| @@ -16,19 +17,21 @@ use embassy_stm32::Peripherals; | |||
| 16 | 17 | ||
| 17 | #[embassy::main] | 18 | #[embassy::main] |
| 18 | async fn main(_spawner: Spawner, p: Peripherals) { | 19 | async 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] |
| 11 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-traits = { version = "0.1.0", path = "../../embassy-traits" } | 12 | embassy-traits = { version = "0.1.0", path = "../../embassy-traits" } |
| 13 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "usb-otg"] } | 13 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } |
| 14 | 14 | ||
| 15 | defmt = "0.3" | 15 | defmt = "0.3" |
| 16 | defmt-rtt = "0.3" | 16 | defmt-rtt = "0.3" |
diff --git a/examples/stm32l4/src/bin/usb_uart.rs b/examples/stm32l4/src/bin/usb_uart.rs deleted file mode 100644 index 878309550..000000000 --- a/examples/stm32l4/src/bin/usb_uart.rs +++ /dev/null | |||
| @@ -1,115 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt_rtt as _; // global logger | ||
| 6 | use panic_probe as _; | ||
| 7 | |||
| 8 | use defmt::{info, unwrap}; | ||
| 9 | use defmt_rtt as _; // global logger | ||
| 10 | use embassy::interrupt::InterruptExt; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use panic_probe as _; // print out panic messages | ||
| 13 | |||
| 14 | use embassy::executor::Spawner; | ||
| 15 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 16 | use embassy_stm32::pac::pwr::vals::Usv; | ||
| 17 | use embassy_stm32::pac::{PWR, RCC}; | ||
| 18 | use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; | ||
| 19 | use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial}; | ||
| 20 | use embassy_stm32::{interrupt, Config, Peripherals}; | ||
| 21 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 22 | |||
| 23 | static mut EP_MEMORY: [u32; 2048] = [0; 2048]; | ||
| 24 | |||
| 25 | // USB requires at least 48 MHz clock | ||
| 26 | fn config() -> Config { | ||
| 27 | let mut config = Config::default(); | ||
| 28 | // set up a 80Mhz clock | ||
| 29 | config.rcc.mux = ClockSrc::PLL( | ||
| 30 | PLLSource::HSI16, | ||
| 31 | PLLClkDiv::Div2, | ||
| 32 | PLLSrcDiv::Div2, | ||
| 33 | PLLMul::Mul20, | ||
| 34 | None, | ||
| 35 | ); | ||
| 36 | // enable HSI48 clock for USB | ||
| 37 | config.rcc.hsi48 = true; | ||
| 38 | config | ||
| 39 | } | ||
| 40 | |||
| 41 | #[embassy::main(config = "config()")] | ||
| 42 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 43 | // Enable PWR peripheral | ||
| 44 | unsafe { RCC.apb1enr1().modify(|w| w.set_pwren(true)) }; | ||
| 45 | unsafe { PWR.cr2().modify(|w| w.set_usv(Usv::VALID)) } | ||
| 46 | |||
| 47 | let mut rx_buffer = [0u8; 64]; | ||
| 48 | // we send back input + cr + lf | ||
| 49 | let mut tx_buffer = [0u8; 66]; | ||
| 50 | |||
| 51 | let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11); | ||
| 52 | let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY }); | ||
| 53 | |||
| 54 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 55 | |||
| 56 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 57 | .manufacturer("Fake company") | ||
| 58 | .product("Serial port") | ||
| 59 | .serial_number("TEST") | ||
| 60 | .device_class(0x02) | ||
| 61 | .build(); | ||
| 62 | |||
| 63 | let irq = interrupt::take!(OTG_FS); | ||
| 64 | irq.set_priority(interrupt::Priority::P3); | ||
| 65 | |||
| 66 | let mut state = State::new(); | ||
| 67 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 68 | pin_mut!(usb); | ||
| 69 | |||
| 70 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 71 | |||
| 72 | info!("usb initialized!"); | ||
| 73 | |||
| 74 | unwrap!( | ||
| 75 | writer | ||
| 76 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 77 | .await | ||
| 78 | ); | ||
| 79 | |||
| 80 | let mut buf = [0u8; 64]; | ||
| 81 | loop { | ||
| 82 | let mut n = 0; | ||
| 83 | |||
| 84 | async { | ||
| 85 | loop { | ||
| 86 | let char = unwrap!(reader.read_byte().await); | ||
| 87 | |||
| 88 | if char == b'\r' || char == b'\n' { | ||
| 89 | break; | ||
| 90 | } | ||
| 91 | |||
| 92 | buf[n] = char; | ||
| 93 | n += 1; | ||
| 94 | |||
| 95 | // stop if we're out of room | ||
| 96 | if n == buf.len() { | ||
| 97 | break; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | .await; | ||
| 102 | |||
| 103 | if n > 0 { | ||
| 104 | for char in buf[..n].iter_mut() { | ||
| 105 | // upper case | ||
| 106 | if 0x61 <= *char && *char <= 0x7a { | ||
| 107 | *char &= !0x20; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 111 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 112 | unwrap!(writer.flush().await); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
diff --git a/stm32-data b/stm32-data | |||
| Subproject 9abfa9d2b51e6071fdc7e680b4a171e4fa20c2f | Subproject b2d7a9f5de7dc3ae17c87c1ff94e13a822d18e7 | ||
