Merge branch 'embassy-rs:master' into qdec
This commit is contained in:
commit
1ca5475010
@ -14,7 +14,7 @@ The bootloader supports both internal and external flash by relying on the `embe
|
|||||||
The bootloader supports
|
The bootloader supports
|
||||||
|
|
||||||
* nRF52 with and without softdevice
|
* nRF52 with and without softdevice
|
||||||
* STM32 L4, WB, WL, L1 and L0
|
* STM32 L4, WB, WL, L1, L0, F3, F7 and H7
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
authors = [
|
authors = [
|
||||||
"Ulf Lilleengen <lulf@redhat.com>",
|
"Ulf Lilleengen <lulf@redhat.com>",
|
||||||
]
|
]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
name = "embassy-boot-stm32"
|
name = "embassy-boot-stm32"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Bootloader for STM32 chips"
|
description = "Bootloader for STM32 chips"
|
||||||
|
@ -67,7 +67,7 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
|
|||||||
[(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
|
[(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
|
||||||
{
|
{
|
||||||
match self.boot.prepare_boot(flash) {
|
match self.boot.prepare_boot(flash) {
|
||||||
Ok(_) => self.boot.boot_address(),
|
Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(),
|
||||||
Err(_) => panic!("boot prepare error!"),
|
Err(_) => panic!("boot prepare error!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,5 +12,4 @@ embassy = { version = "0.1.0", path = "../embassy" }
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
cortex-m = "0.7.3"
|
cortex-m = "0.7.3"
|
||||||
usb-device = "0.2.8"
|
|
||||||
num-traits = { version = "0.2.14", default-features = false }
|
num-traits = { version = "0.2.14", default-features = false }
|
||||||
|
@ -10,7 +10,6 @@ mod macros;
|
|||||||
pub mod peripheral;
|
pub mod peripheral;
|
||||||
pub mod ratio;
|
pub mod ratio;
|
||||||
pub mod ring_buffer;
|
pub mod ring_buffer;
|
||||||
pub mod usb;
|
|
||||||
|
|
||||||
/// Low power blocking wait loop using WFE/SEV.
|
/// Low power blocking wait loop using WFE/SEV.
|
||||||
pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) {
|
pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) {
|
||||||
|
@ -1,338 +0,0 @@
|
|||||||
// Copied from https://github.com/mvirkkunen/usbd-serial
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use core::convert::TryInto;
|
|
||||||
use core::mem;
|
|
||||||
use usb_device::class_prelude::*;
|
|
||||||
use usb_device::Result;
|
|
||||||
|
|
||||||
/// This should be used as `device_class` when building the `UsbDevice`.
|
|
||||||
pub const USB_CLASS_CDC: u8 = 0x02;
|
|
||||||
|
|
||||||
const USB_CLASS_CDC_DATA: u8 = 0x0a;
|
|
||||||
const CDC_SUBCLASS_ACM: u8 = 0x02;
|
|
||||||
const CDC_PROTOCOL_NONE: u8 = 0x00;
|
|
||||||
|
|
||||||
const CS_INTERFACE: u8 = 0x24;
|
|
||||||
const CDC_TYPE_HEADER: u8 = 0x00;
|
|
||||||
const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01;
|
|
||||||
const CDC_TYPE_ACM: u8 = 0x02;
|
|
||||||
const CDC_TYPE_UNION: u8 = 0x06;
|
|
||||||
|
|
||||||
const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
|
|
||||||
#[allow(unused)]
|
|
||||||
const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
|
|
||||||
const REQ_SET_LINE_CODING: u8 = 0x20;
|
|
||||||
const REQ_GET_LINE_CODING: u8 = 0x21;
|
|
||||||
const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
|
|
||||||
|
|
||||||
/// Packet level implementation of a CDC-ACM serial port.
|
|
||||||
///
|
|
||||||
/// This class can be used directly and it has the least overhead due to directly reading and
|
|
||||||
/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial
|
|
||||||
/// port. The following constraints must be followed if you use this class directly:
|
|
||||||
///
|
|
||||||
/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the
|
|
||||||
/// method will return a `WouldBlock` error if there is no packet to be read.
|
|
||||||
/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the
|
|
||||||
/// method will return a `WouldBlock` error if the previous packet has not been sent yet.
|
|
||||||
/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
|
|
||||||
/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
|
|
||||||
/// can be sent if there is no other data to send. This is because USB bulk transactions must be
|
|
||||||
/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
|
|
||||||
pub struct CdcAcmClass<'a, B: UsbBus> {
|
|
||||||
comm_if: InterfaceNumber,
|
|
||||||
comm_ep: EndpointIn<'a, B>,
|
|
||||||
data_if: InterfaceNumber,
|
|
||||||
read_ep: EndpointOut<'a, B>,
|
|
||||||
write_ep: EndpointIn<'a, B>,
|
|
||||||
line_coding: LineCoding,
|
|
||||||
dtr: bool,
|
|
||||||
rts: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: UsbBus> CdcAcmClass<'_, B> {
|
|
||||||
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
|
|
||||||
/// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
|
|
||||||
pub fn new(alloc: &UsbBusAllocator<B>, max_packet_size: u16) -> CdcAcmClass<'_, B> {
|
|
||||||
CdcAcmClass {
|
|
||||||
comm_if: alloc.interface(),
|
|
||||||
comm_ep: alloc.interrupt(8, 255),
|
|
||||||
data_if: alloc.interface(),
|
|
||||||
read_ep: alloc.bulk(max_packet_size),
|
|
||||||
write_ep: alloc.bulk(max_packet_size),
|
|
||||||
line_coding: LineCoding {
|
|
||||||
stop_bits: StopBits::One,
|
|
||||||
data_bits: 8,
|
|
||||||
parity_type: ParityType::None,
|
|
||||||
data_rate: 8_000,
|
|
||||||
},
|
|
||||||
dtr: false,
|
|
||||||
rts: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the maximum packet size in bytes.
|
|
||||||
pub fn max_packet_size(&self) -> u16 {
|
|
||||||
// The size is the same for both endpoints.
|
|
||||||
self.read_ep.max_packet_size()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the current line coding. The line coding contains information that's mainly relevant
|
|
||||||
/// for USB to UART serial port emulators, and can be ignored if not relevant.
|
|
||||||
pub fn line_coding(&self) -> &LineCoding {
|
|
||||||
&self.line_coding
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the DTR (data terminal ready) state
|
|
||||||
pub fn dtr(&self) -> bool {
|
|
||||||
self.dtr
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the RTS (request to send) state
|
|
||||||
pub fn rts(&self) -> bool {
|
|
||||||
self.rts
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes a single packet into the IN endpoint.
|
|
||||||
pub fn write_packet(&mut self, data: &[u8]) -> Result<usize> {
|
|
||||||
self.write_ep.write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a single packet from the OUT endpoint.
|
|
||||||
pub fn read_packet(&mut self, data: &mut [u8]) -> Result<usize> {
|
|
||||||
self.read_ep.read(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the address of the IN endpoint.
|
|
||||||
pub fn write_ep_address(&self) -> EndpointAddress {
|
|
||||||
self.write_ep.address()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the address of the OUT endpoint.
|
|
||||||
pub fn read_ep_address(&self) -> EndpointAddress {
|
|
||||||
self.read_ep.address()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> {
|
|
||||||
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
|
|
||||||
writer.iad(
|
|
||||||
self.comm_if,
|
|
||||||
2,
|
|
||||||
USB_CLASS_CDC,
|
|
||||||
CDC_SUBCLASS_ACM,
|
|
||||||
CDC_PROTOCOL_NONE,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.interface(
|
|
||||||
self.comm_if,
|
|
||||||
USB_CLASS_CDC,
|
|
||||||
CDC_SUBCLASS_ACM,
|
|
||||||
CDC_PROTOCOL_NONE,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.write(
|
|
||||||
CS_INTERFACE,
|
|
||||||
&[
|
|
||||||
CDC_TYPE_HEADER, // bDescriptorSubtype
|
|
||||||
0x10,
|
|
||||||
0x01, // bcdCDC (1.10)
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.write(
|
|
||||||
CS_INTERFACE,
|
|
||||||
&[
|
|
||||||
CDC_TYPE_ACM, // bDescriptorSubtype
|
|
||||||
0x00, // bmCapabilities
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.write(
|
|
||||||
CS_INTERFACE,
|
|
||||||
&[
|
|
||||||
CDC_TYPE_UNION, // bDescriptorSubtype
|
|
||||||
self.comm_if.into(), // bControlInterface
|
|
||||||
self.data_if.into(), // bSubordinateInterface
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.write(
|
|
||||||
CS_INTERFACE,
|
|
||||||
&[
|
|
||||||
CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype
|
|
||||||
0x00, // bmCapabilities
|
|
||||||
self.data_if.into(), // bDataInterface
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.endpoint(&self.comm_ep)?;
|
|
||||||
|
|
||||||
writer.interface(self.data_if, USB_CLASS_CDC_DATA, 0x00, 0x00)?;
|
|
||||||
|
|
||||||
writer.endpoint(&self.write_ep)?;
|
|
||||||
writer.endpoint(&self.read_ep)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) {
|
|
||||||
self.line_coding = LineCoding::default();
|
|
||||||
self.dtr = false;
|
|
||||||
self.rts = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn control_in(&mut self, xfer: ControlIn<B>) {
|
|
||||||
let req = xfer.request();
|
|
||||||
|
|
||||||
if !(req.request_type == control::RequestType::Class
|
|
||||||
&& req.recipient == control::Recipient::Interface
|
|
||||||
&& req.index == u8::from(self.comm_if) as u16)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match req.request {
|
|
||||||
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
|
|
||||||
REQ_GET_LINE_CODING if req.length == 7 => {
|
|
||||||
xfer.accept(|data| {
|
|
||||||
data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
|
|
||||||
data[4] = self.line_coding.stop_bits as u8;
|
|
||||||
data[5] = self.line_coding.parity_type as u8;
|
|
||||||
data[6] = self.line_coding.data_bits;
|
|
||||||
|
|
||||||
Ok(7)
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
xfer.reject().ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn control_out(&mut self, xfer: ControlOut<B>) {
|
|
||||||
let req = xfer.request();
|
|
||||||
|
|
||||||
if !(req.request_type == control::RequestType::Class
|
|
||||||
&& req.recipient == control::Recipient::Interface
|
|
||||||
&& req.index == u8::from(self.comm_if) as u16)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match req.request {
|
|
||||||
REQ_SEND_ENCAPSULATED_COMMAND => {
|
|
||||||
// We don't actually support encapsulated commands but pretend we do for standards
|
|
||||||
// compatibility.
|
|
||||||
xfer.accept().ok();
|
|
||||||
}
|
|
||||||
REQ_SET_LINE_CODING if xfer.data().len() >= 7 => {
|
|
||||||
self.line_coding.data_rate =
|
|
||||||
u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap());
|
|
||||||
self.line_coding.stop_bits = xfer.data()[4].into();
|
|
||||||
self.line_coding.parity_type = xfer.data()[5].into();
|
|
||||||
self.line_coding.data_bits = xfer.data()[6];
|
|
||||||
|
|
||||||
xfer.accept().ok();
|
|
||||||
}
|
|
||||||
REQ_SET_CONTROL_LINE_STATE => {
|
|
||||||
self.dtr = (req.value & 0x0001) != 0;
|
|
||||||
self.rts = (req.value & 0x0002) != 0;
|
|
||||||
|
|
||||||
xfer.accept().ok();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
xfer.reject().ok();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Number of stop bits for LineCoding
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum StopBits {
|
|
||||||
/// 1 stop bit
|
|
||||||
One = 0,
|
|
||||||
|
|
||||||
/// 1.5 stop bits
|
|
||||||
OnePointFive = 1,
|
|
||||||
|
|
||||||
/// 2 stop bits
|
|
||||||
Two = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u8> for StopBits {
|
|
||||||
fn from(value: u8) -> Self {
|
|
||||||
if value <= 2 {
|
|
||||||
unsafe { mem::transmute(value) }
|
|
||||||
} else {
|
|
||||||
StopBits::One
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parity for LineCoding
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum ParityType {
|
|
||||||
None = 0,
|
|
||||||
Odd = 1,
|
|
||||||
Event = 2,
|
|
||||||
Mark = 3,
|
|
||||||
Space = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u8> for ParityType {
|
|
||||||
fn from(value: u8) -> Self {
|
|
||||||
if value <= 4 {
|
|
||||||
unsafe { mem::transmute(value) }
|
|
||||||
} else {
|
|
||||||
ParityType::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Line coding parameters
|
|
||||||
///
|
|
||||||
/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
|
|
||||||
/// be ignored if you don't plan to interface with a physical UART.
|
|
||||||
pub struct LineCoding {
|
|
||||||
stop_bits: StopBits,
|
|
||||||
data_bits: u8,
|
|
||||||
parity_type: ParityType,
|
|
||||||
data_rate: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LineCoding {
|
|
||||||
/// Gets the number of stop bits for UART communication.
|
|
||||||
pub fn stop_bits(&self) -> StopBits {
|
|
||||||
self.stop_bits
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the number of data bits for UART communication.
|
|
||||||
pub fn data_bits(&self) -> u8 {
|
|
||||||
self.data_bits
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the parity type for UART communication.
|
|
||||||
pub fn parity_type(&self) -> ParityType {
|
|
||||||
self.parity_type
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the data rate in bits per second for UART communication.
|
|
||||||
pub fn data_rate(&self) -> u32 {
|
|
||||||
self.data_rate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for LineCoding {
|
|
||||||
fn default() -> Self {
|
|
||||||
LineCoding {
|
|
||||||
stop_bits: StopBits::One,
|
|
||||||
data_bits: 8,
|
|
||||||
parity_type: ParityType::None,
|
|
||||||
data_rate: 8_000,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
use core::cell::RefCell;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::pin::Pin;
|
|
||||||
|
|
||||||
use usb_device::bus::UsbBus;
|
|
||||||
use usb_device::class::UsbClass;
|
|
||||||
use usb_device::device::UsbDevice;
|
|
||||||
|
|
||||||
mod cdc_acm;
|
|
||||||
pub mod usb_serial;
|
|
||||||
|
|
||||||
use crate::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
|
||||||
use embassy::interrupt::Interrupt;
|
|
||||||
pub use usb_serial::{ReadInterface, UsbSerial, WriteInterface};
|
|
||||||
|
|
||||||
/// Marker trait to mark an interrupt to be used with the [`Usb`] abstraction.
|
|
||||||
pub unsafe trait USBInterrupt: Interrupt + Send {}
|
|
||||||
|
|
||||||
pub struct State<'bus, B, T, I>(StateStorage<StateInner<'bus, B, T, I>>)
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt;
|
|
||||||
|
|
||||||
impl<'bus, B, T, I> State<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(StateStorage::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct StateInner<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
device: UsbDevice<'bus, B>,
|
|
||||||
pub(crate) classes: T,
|
|
||||||
_interrupt: PhantomData<I>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Usb<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
// Don't you dare moving out `PeripheralMutex`
|
|
||||||
inner: RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, I>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, B, T, I> Usb<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
/// safety: the returned instance is not leak-safe
|
|
||||||
pub unsafe fn new<S: IntoClassSet<B, T>>(
|
|
||||||
state: &'bus mut State<'bus, B, T, I>,
|
|
||||||
device: UsbDevice<'bus, B>,
|
|
||||||
class_set: S,
|
|
||||||
irq: I,
|
|
||||||
) -> Self {
|
|
||||||
let mutex = PeripheralMutex::new_unchecked(irq, &mut state.0, || StateInner {
|
|
||||||
device,
|
|
||||||
classes: class_set.into_class_set(),
|
|
||||||
_interrupt: PhantomData,
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
inner: RefCell::new(mutex),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B> + SerialState<'bus, 'c, B, Index0>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
/// Take a serial class that was passed as the first class in a tuple
|
|
||||||
pub fn take_serial_0<'a>(
|
|
||||||
self: Pin<&'a Self>,
|
|
||||||
) -> (
|
|
||||||
ReadInterface<'a, 'bus, 'c, Index0, B, T, I>,
|
|
||||||
WriteInterface<'a, 'bus, 'c, Index0, B, T, I>,
|
|
||||||
) {
|
|
||||||
let this = self.get_ref();
|
|
||||||
|
|
||||||
let r = ReadInterface {
|
|
||||||
inner: &this.inner,
|
|
||||||
_buf_lifetime: PhantomData,
|
|
||||||
_index: PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
let w = WriteInterface {
|
|
||||||
inner: &this.inner,
|
|
||||||
_buf_lifetime: PhantomData,
|
|
||||||
_index: PhantomData,
|
|
||||||
};
|
|
||||||
(r, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B> + SerialState<'bus, 'c, B, Index1>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
/// Take a serial class that was passed as the second class in a tuple
|
|
||||||
pub fn take_serial_1<'a>(
|
|
||||||
self: Pin<&'a Self>,
|
|
||||||
) -> (
|
|
||||||
ReadInterface<'a, 'bus, 'c, Index1, B, T, I>,
|
|
||||||
WriteInterface<'a, 'bus, 'c, Index1, B, T, I>,
|
|
||||||
) {
|
|
||||||
let this = self.get_ref();
|
|
||||||
|
|
||||||
let r = ReadInterface {
|
|
||||||
inner: &this.inner,
|
|
||||||
_buf_lifetime: PhantomData,
|
|
||||||
_index: PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
let w = WriteInterface {
|
|
||||||
inner: &this.inner,
|
|
||||||
_buf_lifetime: PhantomData,
|
|
||||||
_index: PhantomData,
|
|
||||||
};
|
|
||||||
(r, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, B, T, I> PeripheralState for StateInner<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
type Interrupt = I;
|
|
||||||
fn on_interrupt(&mut self) {
|
|
||||||
self.classes.poll_all(&mut self.device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ClassSet<B: UsbBus>: Send {
|
|
||||||
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait IntoClassSet<B: UsbBus, C: ClassSet<B>> {
|
|
||||||
fn into_class_set(self) -> C;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ClassSet1<B, C1>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
C1: UsbClass<B>,
|
|
||||||
{
|
|
||||||
class: C1,
|
|
||||||
_bus: PhantomData<B>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ClassSet2<B, C1, C2>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
C1: UsbClass<B>,
|
|
||||||
C2: UsbClass<B>,
|
|
||||||
{
|
|
||||||
class1: C1,
|
|
||||||
class2: C2,
|
|
||||||
_bus: PhantomData<B>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The first class into a [`ClassSet`]
|
|
||||||
pub struct Index0;
|
|
||||||
|
|
||||||
/// The second class into a [`ClassSet`]
|
|
||||||
pub struct Index1;
|
|
||||||
|
|
||||||
impl<B, C1> ClassSet<B> for ClassSet1<B, C1>
|
|
||||||
where
|
|
||||||
B: UsbBus + Send,
|
|
||||||
C1: UsbClass<B> + Send,
|
|
||||||
{
|
|
||||||
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
|
|
||||||
device.poll(&mut [&mut self.class])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, C1, C2> ClassSet<B> for ClassSet2<B, C1, C2>
|
|
||||||
where
|
|
||||||
B: UsbBus + Send,
|
|
||||||
C1: UsbClass<B> + Send,
|
|
||||||
C2: UsbClass<B> + Send,
|
|
||||||
{
|
|
||||||
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
|
|
||||||
device.poll(&mut [&mut self.class1, &mut self.class2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, C1> IntoClassSet<B, ClassSet1<B, C1>> for C1
|
|
||||||
where
|
|
||||||
B: UsbBus + Send,
|
|
||||||
C1: UsbClass<B> + Send,
|
|
||||||
{
|
|
||||||
fn into_class_set(self) -> ClassSet1<B, C1> {
|
|
||||||
ClassSet1 {
|
|
||||||
class: self,
|
|
||||||
_bus: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, C1, C2> IntoClassSet<B, ClassSet2<B, C1, C2>> for (C1, C2)
|
|
||||||
where
|
|
||||||
B: UsbBus + Send,
|
|
||||||
C1: UsbClass<B> + Send,
|
|
||||||
C2: UsbClass<B> + Send,
|
|
||||||
{
|
|
||||||
fn into_class_set(self) -> ClassSet2<B, C1, C2> {
|
|
||||||
ClassSet2 {
|
|
||||||
class1: self.0,
|
|
||||||
class2: self.1,
|
|
||||||
_bus: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait for a USB State that has a serial class inside
|
|
||||||
pub trait SerialState<'bus, 'a, B: UsbBus, I> {
|
|
||||||
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B: UsbBus> SerialState<'bus, 'a, B, Index0>
|
|
||||||
for ClassSet1<B, UsbSerial<'bus, 'a, B>>
|
|
||||||
{
|
|
||||||
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
|
|
||||||
&mut self.class
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B, C2> SerialState<'bus, 'a, B, Index0> for ClassSet2<B, UsbSerial<'bus, 'a, B>, C2>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
C2: UsbClass<B>,
|
|
||||||
{
|
|
||||||
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
|
|
||||||
&mut self.class1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B, C1> SerialState<'bus, 'a, B, Index1> for ClassSet2<B, C1, UsbSerial<'bus, 'a, B>>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
C1: UsbClass<B>,
|
|
||||||
{
|
|
||||||
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
|
|
||||||
&mut self.class2
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,345 +0,0 @@
|
|||||||
use core::cell::RefCell;
|
|
||||||
use core::marker::{PhantomData, Unpin};
|
|
||||||
use core::pin::Pin;
|
|
||||||
use core::task::{Context, Poll};
|
|
||||||
|
|
||||||
use embassy::io::{self, AsyncBufRead, AsyncWrite};
|
|
||||||
use embassy::waitqueue::WakerRegistration;
|
|
||||||
use usb_device::bus::UsbBus;
|
|
||||||
use usb_device::class_prelude::*;
|
|
||||||
use usb_device::UsbError;
|
|
||||||
|
|
||||||
use super::cdc_acm::CdcAcmClass;
|
|
||||||
use super::StateInner;
|
|
||||||
use crate::peripheral::PeripheralMutex;
|
|
||||||
use crate::ring_buffer::RingBuffer;
|
|
||||||
use crate::usb::{ClassSet, SerialState, USBInterrupt};
|
|
||||||
|
|
||||||
pub struct ReadInterface<'a, 'bus, 'c, I, B, T, INT>
|
|
||||||
where
|
|
||||||
I: Unpin,
|
|
||||||
B: UsbBus,
|
|
||||||
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
|
|
||||||
INT: USBInterrupt,
|
|
||||||
{
|
|
||||||
// Don't you dare moving out `PeripheralMutex`
|
|
||||||
pub(crate) inner: &'a RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, INT>>>,
|
|
||||||
pub(crate) _buf_lifetime: PhantomData<&'c T>,
|
|
||||||
pub(crate) _index: PhantomData<I>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write interface for USB CDC_ACM
|
|
||||||
///
|
|
||||||
/// This interface is buffered, meaning that after the write returns the bytes might not be fully
|
|
||||||
/// on the wire just yet
|
|
||||||
pub struct WriteInterface<'a, 'bus, 'c, I, B, T, INT>
|
|
||||||
where
|
|
||||||
I: Unpin,
|
|
||||||
B: UsbBus,
|
|
||||||
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
|
|
||||||
INT: USBInterrupt,
|
|
||||||
{
|
|
||||||
// Don't you dare moving out `PeripheralMutex`
|
|
||||||
pub(crate) inner: &'a RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, INT>>>,
|
|
||||||
pub(crate) _buf_lifetime: PhantomData<&'c T>,
|
|
||||||
pub(crate) _index: PhantomData<I>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'bus, 'c, I, B, T, INT> AsyncBufRead for ReadInterface<'a, 'bus, 'c, I, B, T, INT>
|
|
||||||
where
|
|
||||||
I: Unpin,
|
|
||||||
B: UsbBus,
|
|
||||||
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
|
|
||||||
INT: USBInterrupt,
|
|
||||||
{
|
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let mut mutex = this.inner.borrow_mut();
|
|
||||||
mutex.with(|state| {
|
|
||||||
let serial = state.classes.get_serial();
|
|
||||||
let serial = Pin::new(serial);
|
|
||||||
|
|
||||||
match serial.poll_fill_buf(cx) {
|
|
||||||
Poll::Ready(Ok(buf)) => {
|
|
||||||
let buf: &[u8] = buf;
|
|
||||||
// NOTE(unsafe) This part of the buffer won't be modified until the user calls
|
|
||||||
// consume, which will invalidate this ref
|
|
||||||
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
|
|
||||||
Poll::Ready(Ok(buf))
|
|
||||||
}
|
|
||||||
Poll::Ready(Err(_)) => Poll::Ready(Err(io::Error::Other)),
|
|
||||||
Poll::Pending => Poll::Pending,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let mut mutex = this.inner.borrow_mut();
|
|
||||||
mutex.with(|state| {
|
|
||||||
let serial = state.classes.get_serial();
|
|
||||||
let serial = Pin::new(serial);
|
|
||||||
|
|
||||||
serial.consume(amt);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'bus, 'c, I, B, T, INT> AsyncWrite for WriteInterface<'a, 'bus, 'c, I, B, T, INT>
|
|
||||||
where
|
|
||||||
I: Unpin,
|
|
||||||
B: UsbBus,
|
|
||||||
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
|
|
||||||
INT: USBInterrupt,
|
|
||||||
{
|
|
||||||
fn poll_write(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let mut mutex = this.inner.borrow_mut();
|
|
||||||
mutex.with(|state| {
|
|
||||||
let serial = state.classes.get_serial();
|
|
||||||
let serial = Pin::new(serial);
|
|
||||||
|
|
||||||
serial.poll_write(cx, buf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let mut mutex = this.inner.borrow_mut();
|
|
||||||
mutex.with(|state| {
|
|
||||||
let serial = state.classes.get_serial();
|
|
||||||
let serial = Pin::new(serial);
|
|
||||||
|
|
||||||
serial.poll_flush(cx)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UsbSerial<'bus, 'a, B: UsbBus> {
|
|
||||||
inner: CdcAcmClass<'bus, B>,
|
|
||||||
read_buf: RingBuffer<'a>,
|
|
||||||
write_buf: RingBuffer<'a>,
|
|
||||||
read_waker: WakerRegistration,
|
|
||||||
write_waker: WakerRegistration,
|
|
||||||
write_state: WriteState,
|
|
||||||
read_error: bool,
|
|
||||||
write_error: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B: UsbBus> AsyncBufRead for UsbSerial<'bus, 'a, B> {
|
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
|
|
||||||
if this.read_error {
|
|
||||||
this.read_error = false;
|
|
||||||
return Poll::Ready(Err(io::Error::Other));
|
|
||||||
}
|
|
||||||
|
|
||||||
let buf = this.read_buf.pop_buf();
|
|
||||||
if buf.is_empty() {
|
|
||||||
this.read_waker.register(cx.waker());
|
|
||||||
return Poll::Pending;
|
|
||||||
}
|
|
||||||
Poll::Ready(Ok(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
||||||
self.get_mut().read_buf.pop(amt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B: UsbBus> AsyncWrite for UsbSerial<'bus, 'a, B> {
|
|
||||||
fn poll_write(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
|
|
||||||
if this.write_error {
|
|
||||||
this.write_error = false;
|
|
||||||
return Poll::Ready(Err(io::Error::Other));
|
|
||||||
}
|
|
||||||
|
|
||||||
let write_buf = this.write_buf.push_buf();
|
|
||||||
if write_buf.is_empty() {
|
|
||||||
trace!("buf full, registering waker");
|
|
||||||
this.write_waker.register(cx.waker());
|
|
||||||
return Poll::Pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
let count = write_buf.len().min(buf.len());
|
|
||||||
write_buf[..count].copy_from_slice(&buf[..count]);
|
|
||||||
this.write_buf.push(count);
|
|
||||||
|
|
||||||
this.flush_write();
|
|
||||||
Poll::Ready(Ok(count))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Keeps track of the type of the last written packet.
|
|
||||||
enum WriteState {
|
|
||||||
/// No packets in-flight
|
|
||||||
Idle,
|
|
||||||
|
|
||||||
/// Short packet currently in-flight
|
|
||||||
Short,
|
|
||||||
|
|
||||||
/// Full packet current in-flight. A full packet must be followed by a short packet for the host
|
|
||||||
/// OS to see the transaction. The data is the number of subsequent full packets sent so far. A
|
|
||||||
/// short packet is forced every SHORT_PACKET_INTERVAL packets so that the OS sees data in a
|
|
||||||
/// timely manner.
|
|
||||||
Full(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B: UsbBus> UsbSerial<'bus, 'a, B> {
|
|
||||||
pub fn new(
|
|
||||||
alloc: &'bus UsbBusAllocator<B>,
|
|
||||||
read_buf: &'a mut [u8],
|
|
||||||
write_buf: &'a mut [u8],
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: CdcAcmClass::new(alloc, 64),
|
|
||||||
read_buf: RingBuffer::new(read_buf),
|
|
||||||
write_buf: RingBuffer::new(write_buf),
|
|
||||||
read_waker: WakerRegistration::new(),
|
|
||||||
write_waker: WakerRegistration::new(),
|
|
||||||
write_state: WriteState::Idle,
|
|
||||||
read_error: false,
|
|
||||||
write_error: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_write(&mut self) {
|
|
||||||
/// If this many full size packets have been sent in a row, a short packet will be sent so that the
|
|
||||||
/// host sees the data in a timely manner.
|
|
||||||
const SHORT_PACKET_INTERVAL: usize = 10;
|
|
||||||
|
|
||||||
let full_size_packets = match self.write_state {
|
|
||||||
WriteState::Full(c) => c,
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let ep_size = self.inner.max_packet_size() as usize;
|
|
||||||
let max_size = if full_size_packets > SHORT_PACKET_INTERVAL {
|
|
||||||
ep_size - 1
|
|
||||||
} else {
|
|
||||||
ep_size
|
|
||||||
};
|
|
||||||
|
|
||||||
let buf = {
|
|
||||||
let buf = self.write_buf.pop_buf();
|
|
||||||
if buf.len() > max_size {
|
|
||||||
&buf[..max_size]
|
|
||||||
} else {
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if !buf.is_empty() {
|
|
||||||
trace!("writing packet len {}", buf.len());
|
|
||||||
let count = match self.inner.write_packet(buf) {
|
|
||||||
Ok(c) => {
|
|
||||||
trace!("write packet: OK {}", c);
|
|
||||||
c
|
|
||||||
}
|
|
||||||
Err(UsbError::WouldBlock) => {
|
|
||||||
trace!("write packet: WouldBlock");
|
|
||||||
0
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
trace!("write packet: error");
|
|
||||||
self.write_error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if buf.len() == ep_size {
|
|
||||||
self.write_state = WriteState::Full(full_size_packets + 1);
|
|
||||||
} else {
|
|
||||||
self.write_state = WriteState::Short;
|
|
||||||
}
|
|
||||||
self.write_buf.pop(count);
|
|
||||||
} else if full_size_packets > 0 {
|
|
||||||
trace!("writing empty packet");
|
|
||||||
match self.inner.write_packet(&[]) {
|
|
||||||
Ok(_) => {
|
|
||||||
trace!("write empty packet: OK");
|
|
||||||
}
|
|
||||||
Err(UsbError::WouldBlock) => {
|
|
||||||
trace!("write empty packet: WouldBlock");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
trace!("write empty packet: Error");
|
|
||||||
self.write_error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.write_state = WriteState::Idle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B> UsbClass<B> for UsbSerial<'_, '_, B>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
{
|
|
||||||
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<(), UsbError> {
|
|
||||||
self.inner.get_configuration_descriptors(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) {
|
|
||||||
self.inner.reset();
|
|
||||||
self.read_buf.clear();
|
|
||||||
self.write_buf.clear();
|
|
||||||
self.write_state = WriteState::Idle;
|
|
||||||
self.read_waker.wake();
|
|
||||||
self.write_waker.wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
|
|
||||||
trace!("DONE endpoint_in_complete");
|
|
||||||
if addr == self.inner.write_ep_address() {
|
|
||||||
trace!("DONE writing packet, waking");
|
|
||||||
self.write_waker.wake();
|
|
||||||
|
|
||||||
self.flush_write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn endpoint_out(&mut self, addr: EndpointAddress) {
|
|
||||||
if addr == self.inner.read_ep_address() {
|
|
||||||
let buf = self.read_buf.push_buf();
|
|
||||||
let count = match self.inner.read_packet(buf) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(UsbError::WouldBlock) => 0,
|
|
||||||
Err(_) => {
|
|
||||||
self.read_error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if count > 0 {
|
|
||||||
self.read_buf.push(count);
|
|
||||||
self.read_waker.wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn control_in(&mut self, xfer: ControlIn<B>) {
|
|
||||||
self.inner.control_in(xfer);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn control_out(&mut self, xfer: ControlOut<B>) {
|
|
||||||
self.inner.control_out(xfer);
|
|
||||||
}
|
|
||||||
}
|
|
@ -31,12 +31,15 @@ pool-32 = []
|
|||||||
pool-64 = []
|
pool-64 = []
|
||||||
pool-128 = []
|
pool-128 = []
|
||||||
|
|
||||||
|
nightly = ["embedded-io/async"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
embassy = { version = "0.1.0", path = "../embassy" }
|
embassy = { version = "0.1.0", path = "../embassy" }
|
||||||
|
embedded-io = "0.2.0"
|
||||||
|
|
||||||
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
|
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
|
||||||
heapless = { version = "0.7.5", default-features = false }
|
heapless = { version = "0.7.5", default-features = false }
|
||||||
|
@ -4,7 +4,6 @@ use smoltcp::phy::DeviceCapabilities;
|
|||||||
use smoltcp::time::Instant as SmolInstant;
|
use smoltcp::time::Instant as SmolInstant;
|
||||||
|
|
||||||
use crate::packet_pool::PacketBoxExt;
|
use crate::packet_pool::PacketBoxExt;
|
||||||
use crate::Result;
|
|
||||||
use crate::{Packet, PacketBox, PacketBuf};
|
use crate::{Packet, PacketBox, PacketBuf};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
@ -78,9 +77,9 @@ pub struct RxToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl smoltcp::phy::RxToken for RxToken {
|
impl smoltcp::phy::RxToken for RxToken {
|
||||||
fn consume<R, F>(mut self, _timestamp: SmolInstant, f: F) -> Result<R>
|
fn consume<R, F>(mut self, _timestamp: SmolInstant, f: F) -> smoltcp::Result<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
|
||||||
{
|
{
|
||||||
f(&mut self.pkt)
|
f(&mut self.pkt)
|
||||||
}
|
}
|
||||||
@ -92,9 +91,9 @@ pub struct TxToken<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> smoltcp::phy::TxToken for TxToken<'a> {
|
impl<'a> smoltcp::phy::TxToken for TxToken<'a> {
|
||||||
fn consume<R, F>(self, _timestamp: SmolInstant, len: usize, f: F) -> Result<R>
|
fn consume<R, F>(self, _timestamp: SmolInstant, len: usize, f: F) -> smoltcp::Result<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
|
||||||
{
|
{
|
||||||
let mut buf = self.pkt.slice(0..len);
|
let mut buf = self.pkt.slice(0..len);
|
||||||
let r = f(&mut buf)?;
|
let r = f(&mut buf)?;
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![allow(clippy::new_without_default)]
|
#![allow(clippy::new_without_default)]
|
||||||
|
#![cfg_attr(
|
||||||
|
feature = "nightly",
|
||||||
|
feature(generic_associated_types, type_alias_impl_trait)
|
||||||
|
)]
|
||||||
|
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
@ -20,9 +24,7 @@ pub use stack::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "tcp")]
|
#[cfg(feature = "tcp")]
|
||||||
mod tcp_socket;
|
pub mod tcp;
|
||||||
#[cfg(feature = "tcp")]
|
|
||||||
pub use tcp_socket::TcpSocket;
|
|
||||||
|
|
||||||
// smoltcp reexports
|
// smoltcp reexports
|
||||||
pub use smoltcp::phy::{DeviceCapabilities, Medium};
|
pub use smoltcp::phy::{DeviceCapabilities, Medium};
|
||||||
@ -32,4 +34,3 @@ pub use smoltcp::time::Instant as SmolInstant;
|
|||||||
pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
|
pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
|
||||||
pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr};
|
pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr};
|
||||||
pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>;
|
pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>;
|
||||||
pub use smoltcp::{Error, Result};
|
|
||||||
|
67
embassy-net/src/tcp/io_impl.rs
Normal file
67
embassy-net/src/tcp/io_impl.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use core::future::Future;
|
||||||
|
use core::task::Poll;
|
||||||
|
use futures::future::poll_fn;
|
||||||
|
|
||||||
|
use super::{Error, TcpSocket};
|
||||||
|
|
||||||
|
impl<'d> embedded_io::asynch::Read for TcpSocket<'d> {
|
||||||
|
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
// CAUTION: smoltcp semantics around EOF are different to what you'd expect
|
||||||
|
// from posix-like IO, so we have to tweak things here.
|
||||||
|
self.with(|s, _| match s.recv_slice(buf) {
|
||||||
|
// No data ready
|
||||||
|
Ok(0) => {
|
||||||
|
s.register_recv_waker(cx.waker());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
// Data ready!
|
||||||
|
Ok(n) => Poll::Ready(Ok(n)),
|
||||||
|
// EOF
|
||||||
|
Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)),
|
||||||
|
// Connection reset. TODO: this can also be timeouts etc, investigate.
|
||||||
|
Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)),
|
||||||
|
// smoltcp returns no errors other than the above.
|
||||||
|
Err(_) => unreachable!(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
|
||||||
|
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
self.with(|s, _| match s.send_slice(buf) {
|
||||||
|
// Not ready to send (no space in the tx buffer)
|
||||||
|
Ok(0) => {
|
||||||
|
s.register_send_waker(cx.waker());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
// Some data sent
|
||||||
|
Ok(n) => Poll::Ready(Ok(n)),
|
||||||
|
// Connection reset. TODO: this can also be timeouts etc, investigate.
|
||||||
|
Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)),
|
||||||
|
// smoltcp returns no errors other than the above.
|
||||||
|
Err(_) => unreachable!(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||||
|
poll_fn(move |_| {
|
||||||
|
Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,46 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::pin::Pin;
|
use core::task::Poll;
|
||||||
use core::task::{Context, Poll};
|
|
||||||
use embassy::io;
|
|
||||||
use embassy::io::{AsyncBufRead, AsyncWrite};
|
|
||||||
use smoltcp::iface::{Context as SmolContext, SocketHandle};
|
use smoltcp::iface::{Context as SmolContext, SocketHandle};
|
||||||
use smoltcp::socket::TcpSocket as SyncTcpSocket;
|
use smoltcp::socket::TcpSocket as SyncTcpSocket;
|
||||||
use smoltcp::socket::{TcpSocketBuffer, TcpState};
|
use smoltcp::socket::{TcpSocketBuffer, TcpState};
|
||||||
use smoltcp::time::Duration;
|
use smoltcp::time::Duration;
|
||||||
use smoltcp::wire::IpEndpoint;
|
use smoltcp::wire::IpEndpoint;
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
mod io_impl;
|
||||||
|
|
||||||
use super::stack::Stack;
|
use super::stack::Stack;
|
||||||
use crate::{Error, Result};
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum Error {
|
||||||
|
ConnectionReset,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum ConnectError {
|
||||||
|
/// The socket is already connected or listening.
|
||||||
|
InvalidState,
|
||||||
|
/// The remote host rejected the connection with a RST packet.
|
||||||
|
ConnectionReset,
|
||||||
|
/// Connect timed out.
|
||||||
|
TimedOut,
|
||||||
|
/// No route to host.
|
||||||
|
NoRoute,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum AcceptError {
|
||||||
|
/// The socket is already connected or listening.
|
||||||
|
InvalidState,
|
||||||
|
/// Invalid listen port
|
||||||
|
InvalidPort,
|
||||||
|
/// The remote host rejected the connection with a RST packet.
|
||||||
|
ConnectionReset,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TcpSocket<'a> {
|
pub struct TcpSocket<'a> {
|
||||||
handle: SocketHandle,
|
handle: SocketHandle,
|
||||||
@ -37,17 +66,25 @@ impl<'a> TcpSocket<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<()>
|
pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError>
|
||||||
where
|
where
|
||||||
T: Into<IpEndpoint>,
|
T: Into<IpEndpoint>,
|
||||||
{
|
{
|
||||||
let local_port = Stack::with(|stack| stack.get_local_port());
|
let local_port = Stack::with(|stack| stack.get_local_port());
|
||||||
self.with(|s, cx| s.connect(cx, remote_endpoint, local_port))?;
|
match self.with(|s, cx| s.connect(cx, remote_endpoint, local_port)) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(smoltcp::Error::Illegal) => return Err(ConnectError::InvalidState),
|
||||||
|
Err(smoltcp::Error::Unaddressable) => return Err(ConnectError::NoRoute),
|
||||||
|
// smoltcp returns no errors other than the above.
|
||||||
|
Err(_) => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
futures::future::poll_fn(|cx| {
|
futures::future::poll_fn(|cx| {
|
||||||
self.with(|s, _| match s.state() {
|
self.with(|s, _| match s.state() {
|
||||||
TcpState::Closed | TcpState::TimeWait => Poll::Ready(Err(Error::Unaddressable)),
|
TcpState::Closed | TcpState::TimeWait => {
|
||||||
TcpState::Listen => Poll::Ready(Err(Error::Illegal)),
|
Poll::Ready(Err(ConnectError::ConnectionReset))
|
||||||
|
}
|
||||||
|
TcpState::Listen => unreachable!(),
|
||||||
TcpState::SynSent | TcpState::SynReceived => {
|
TcpState::SynSent | TcpState::SynReceived => {
|
||||||
s.register_send_waker(cx.waker());
|
s.register_send_waker(cx.waker());
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
@ -58,11 +95,17 @@ impl<'a> TcpSocket<'a> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<()>
|
pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError>
|
||||||
where
|
where
|
||||||
T: Into<IpEndpoint>,
|
T: Into<IpEndpoint>,
|
||||||
{
|
{
|
||||||
self.with(|s, _| s.listen(local_endpoint))?;
|
match self.with(|s, _| s.listen(local_endpoint)) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(smoltcp::Error::Illegal) => return Err(AcceptError::InvalidState),
|
||||||
|
Err(smoltcp::Error::Unaddressable) => return Err(AcceptError::InvalidPort),
|
||||||
|
// smoltcp returns no errors other than the above.
|
||||||
|
Err(_) => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
futures::future::poll_fn(|cx| {
|
futures::future::poll_fn(|cx| {
|
||||||
self.with(|s, _| match s.state() {
|
self.with(|s, _| match s.state() {
|
||||||
@ -130,11 +173,6 @@ impl<'a> TcpSocket<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_ioerr(_err: Error) -> io::Error {
|
|
||||||
// todo
|
|
||||||
io::Error::Other
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Drop for TcpSocket<'a> {
|
impl<'a> Drop for TcpSocket<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
Stack::with(|stack| {
|
Stack::with(|stack| {
|
||||||
@ -143,63 +181,12 @@ impl<'a> Drop for TcpSocket<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AsyncBufRead for TcpSocket<'a> {
|
impl embedded_io::Error for Error {
|
||||||
fn poll_fill_buf<'z>(
|
fn kind(&self) -> embedded_io::ErrorKind {
|
||||||
self: Pin<&'z mut Self>,
|
embedded_io::ErrorKind::Other
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<io::Result<&'z [u8]>> {
|
|
||||||
self.with(|s, _| match s.peek(1 << 30) {
|
|
||||||
// No data ready
|
|
||||||
Ok(buf) if buf.is_empty() => {
|
|
||||||
s.register_recv_waker(cx.waker());
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
// Data ready!
|
|
||||||
Ok(buf) => {
|
|
||||||
// Safety:
|
|
||||||
// - User can't touch the inner TcpSocket directly at all.
|
|
||||||
// - The socket itself won't touch these bytes until consume() is called, which
|
|
||||||
// requires the user to release this borrow.
|
|
||||||
let buf: &'z [u8] = unsafe { core::mem::transmute(&*buf) };
|
|
||||||
Poll::Ready(Ok(buf))
|
|
||||||
}
|
|
||||||
// EOF
|
|
||||||
Err(Error::Finished) => Poll::Ready(Ok(&[][..])),
|
|
||||||
// Error
|
|
||||||
Err(e) => Poll::Ready(Err(to_ioerr(e))),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
||||||
if amt == 0 {
|
|
||||||
// smoltcp's recv returns Finished if we're at EOF,
|
|
||||||
// even if we're "reading" 0 bytes.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.with(|s, _| s.recv(|_| (amt, ()))).unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AsyncWrite for TcpSocket<'a> {
|
impl<'d> embedded_io::Io for TcpSocket<'d> {
|
||||||
fn poll_write(
|
type Error = Error;
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
self.with(|s, _| match s.send_slice(buf) {
|
|
||||||
// Not ready to send (no space in the tx buffer)
|
|
||||||
Ok(0) => {
|
|
||||||
s.register_send_waker(cx.waker());
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
// Some data sent
|
|
||||||
Ok(n) => Poll::Ready(Ok(n)),
|
|
||||||
// Error
|
|
||||||
Err(e) => Poll::Ready(Err(to_ioerr(e))),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -19,10 +19,10 @@ flavors = [
|
|||||||
|
|
||||||
time = ["embassy/time"]
|
time = ["embassy/time"]
|
||||||
|
|
||||||
defmt = ["dep:defmt", "embassy/defmt", "embassy-usb?/defmt"]
|
defmt = ["dep:defmt", "embassy/defmt", "embassy-usb?/defmt", "embedded-io?/defmt"]
|
||||||
|
|
||||||
# Enable nightly-only features
|
# Enable nightly-only features
|
||||||
nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async"]
|
nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async", "dep:embedded-io"]
|
||||||
|
|
||||||
# Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`.
|
# Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`.
|
||||||
# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version.
|
# 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 }
|
|||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
|
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
|
||||||
embedded-hal-async = { version = "0.1.0-alpha.0", optional = true}
|
embedded-hal-async = { version = "0.1.0-alpha.0", optional = true}
|
||||||
|
embedded-io = { version = "0.2.0", features = ["async"], optional = true }
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
@ -14,18 +14,17 @@
|
|||||||
//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.
|
//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.
|
||||||
|
|
||||||
use core::cmp::min;
|
use core::cmp::min;
|
||||||
|
use core::future::Future;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::mem;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::{Context, Poll};
|
use core::task::Poll;
|
||||||
use embassy::interrupt::InterruptExt;
|
use embassy::interrupt::InterruptExt;
|
||||||
use embassy::io::{AsyncBufRead, AsyncWrite};
|
|
||||||
use embassy::util::Unborrow;
|
use embassy::util::Unborrow;
|
||||||
use embassy::waitqueue::WakerRegistration;
|
use embassy::waitqueue::WakerRegistration;
|
||||||
use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
||||||
use embassy_hal_common::ring_buffer::RingBuffer;
|
use embassy_hal_common::ring_buffer::RingBuffer;
|
||||||
use embassy_hal_common::{low_power_wait_until, unborrow};
|
use embassy_hal_common::{low_power_wait_until, unborrow};
|
||||||
|
use futures::future::poll_fn;
|
||||||
|
|
||||||
use crate::gpio::Pin as GpioPin;
|
use crate::gpio::Pin as GpioPin;
|
||||||
use crate::pac;
|
use crate::pac;
|
||||||
@ -197,46 +196,56 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, U: UarteInstance, T: TimerInstance> AsyncBufRead for BufferedUarte<'d, U, T> {
|
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
|
||||||
fn poll_fill_buf(
|
type Error = core::convert::Infallible;
|
||||||
mut self: Pin<&mut Self>,
|
}
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<embassy::io::Result<&[u8]>> {
|
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
|
||||||
self.inner.with(|state| {
|
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
let mut do_pend = false;
|
||||||
|
let res = self.inner.with(|state| {
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
trace!("poll_read");
|
trace!("poll_read");
|
||||||
|
|
||||||
// We have data ready in buffer? Return it.
|
// We have data ready in buffer? Return it.
|
||||||
let buf = state.rx.pop_buf();
|
let data = state.rx.pop_buf();
|
||||||
if !buf.is_empty() {
|
if !data.is_empty() {
|
||||||
trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len());
|
trace!(" got {:?} {:?}", data.as_ptr() as u32, data.len());
|
||||||
let buf: &[u8] = buf;
|
let len = data.len().min(data.len());
|
||||||
let buf: &[u8] = unsafe { mem::transmute(buf) };
|
buf[..len].copy_from_slice(&data[..len]);
|
||||||
return Poll::Ready(Ok(buf));
|
state.rx.pop(len);
|
||||||
|
do_pend = true;
|
||||||
|
return Poll::Ready(Ok(len));
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(" empty");
|
trace!(" empty");
|
||||||
state.rx_waker.register(cx.waker());
|
state.rx_waker.register(cx.waker());
|
||||||
Poll::<embassy::io::Result<&[u8]>>::Pending
|
Poll::Pending
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(mut self: Pin<&mut Self>, amt: usize) {
|
|
||||||
self.inner.with(|state| {
|
|
||||||
trace!("consume {:?}", amt);
|
|
||||||
state.rx.pop(amt);
|
|
||||||
});
|
});
|
||||||
|
if do_pend {
|
||||||
self.inner.pend();
|
self.inner.pend();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, U: UarteInstance, T: TimerInstance> AsyncWrite for BufferedUarte<'d, U, T> {
|
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write
|
||||||
fn poll_write(
|
for BufferedUarte<'d, U, T>
|
||||||
mut self: Pin<&mut Self>,
|
{
|
||||||
cx: &mut Context<'_>,
|
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||||
buf: &[u8],
|
where
|
||||||
) -> Poll<embassy::io::Result<usize>> {
|
Self: 'a;
|
||||||
let poll = self.inner.with(|state| {
|
|
||||||
|
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
let res = self.inner.with(|state| {
|
||||||
trace!("poll_write: {:?}", buf.len());
|
trace!("poll_write: {:?}", buf.len());
|
||||||
|
|
||||||
let tx_buf = state.tx.push_buf();
|
let tx_buf = state.tx.push_buf();
|
||||||
@ -259,10 +268,16 @@ impl<'d, U: UarteInstance, T: TimerInstance> AsyncWrite for BufferedUarte<'d, U,
|
|||||||
|
|
||||||
self.inner.pend();
|
self.inner.pend();
|
||||||
|
|
||||||
poll
|
res
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<embassy::io::Result<()>> {
|
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
self.inner.with(|state| {
|
self.inner.with(|state| {
|
||||||
trace!("poll_flush");
|
trace!("poll_flush");
|
||||||
|
|
||||||
@ -274,6 +289,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> AsyncWrite for BufferedUarte<'d, U,
|
|||||||
|
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ pub(crate) mod util;
|
|||||||
#[cfg(feature = "_time-driver")]
|
#[cfg(feature = "_time-driver")]
|
||||||
mod time_driver;
|
mod time_driver;
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
pub mod buffered_uarte;
|
pub mod buffered_uarte;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
#[cfg(feature = "gpiote")]
|
#[cfg(feature = "gpiote")]
|
||||||
|
@ -53,17 +53,17 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
|
|||||||
rand_core = "0.6.3"
|
rand_core = "0.6.3"
|
||||||
sdio-host = "0.5.0"
|
sdio-host = "0.5.0"
|
||||||
embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true }
|
embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true }
|
||||||
synopsys-usb-otg = { version = "0.3", features = ["cortex-m", "hs"], optional = true }
|
|
||||||
critical-section = "0.2.5"
|
critical-section = "0.2.5"
|
||||||
bare-metal = "1.0.0"
|
bare-metal = "1.0.0"
|
||||||
atomic-polyfill = "0.1.5"
|
atomic-polyfill = "0.1.5"
|
||||||
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] }
|
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] }
|
||||||
vcell = { version = "0.1.3", optional = true }
|
vcell = "0.1.3"
|
||||||
bxcan = "0.6.2"
|
bxcan = "0.6.2"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
stm32-fmc = "0.2.4"
|
stm32-fmc = "0.2.4"
|
||||||
seq-macro = "0.2.2"
|
seq-macro = "0.2.2"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
|
embedded-io = { version = "0.2.0", features = ["async"], optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
proc-macro2 = "1.0.36"
|
proc-macro2 = "1.0.36"
|
||||||
@ -71,12 +71,12 @@ quote = "1.0.15"
|
|||||||
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
|
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
defmt = ["dep:defmt", "embassy/defmt", "embedded-io?/defmt" ]
|
||||||
sdmmc-rs = ["embedded-sdmmc"]
|
sdmmc-rs = ["embedded-sdmmc"]
|
||||||
net = ["embassy-net", "vcell"]
|
net = ["embassy-net" ]
|
||||||
memory-x = ["stm32-metapac/memory-x"]
|
memory-x = ["stm32-metapac/memory-x"]
|
||||||
subghz = []
|
subghz = []
|
||||||
exti = []
|
exti = []
|
||||||
usb-otg = ["synopsys-usb-otg"]
|
|
||||||
|
|
||||||
# Features starting with `_` are for internal use only. They're not intended
|
# Features starting with `_` are for internal use only. They're not intended
|
||||||
# to be enabled by other crates, and are not covered by semver guarantees.
|
# to be enabled by other crates, and are not covered by semver guarantees.
|
||||||
@ -90,7 +90,7 @@ time-driver-tim12 = ["_time-driver"]
|
|||||||
time-driver-tim15 = ["_time-driver"]
|
time-driver-tim15 = ["_time-driver"]
|
||||||
|
|
||||||
# Enable nightly-only features
|
# Enable nightly-only features
|
||||||
nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async"]
|
nightly = ["embassy/nightly", "embassy-net?/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io"]
|
||||||
|
|
||||||
# Reexport stm32-metapac at `embassy_stm32::pac`.
|
# Reexport stm32-metapac at `embassy_stm32::pac`.
|
||||||
# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version.
|
# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version.
|
||||||
|
@ -244,200 +244,200 @@ fn main() {
|
|||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let signals: HashMap<_, _> = [
|
let signals: HashMap<_, _> = [
|
||||||
// (kind, signal) => (trait, cfgs)
|
// (kind, signal) => trait
|
||||||
(("usart", "TX"), (quote!(crate::usart::TxPin), quote!())),
|
(("usart", "TX"), quote!(crate::usart::TxPin)),
|
||||||
(("usart", "RX"), (quote!(crate::usart::RxPin), quote!())),
|
(("usart", "RX"), quote!(crate::usart::RxPin)),
|
||||||
(("usart", "CTS"), (quote!(crate::usart::CtsPin), quote!())),
|
(("usart", "CTS"), quote!(crate::usart::CtsPin)),
|
||||||
(("usart", "RTS"), (quote!(crate::usart::RtsPin), quote!())),
|
(("usart", "RTS"), quote!(crate::usart::RtsPin)),
|
||||||
(("usart", "CK"), (quote!(crate::usart::CkPin), quote!())),
|
(("usart", "CK"), quote!(crate::usart::CkPin)),
|
||||||
(("usart", "TX"), (quote!(crate::usart::TxPin), quote!())),
|
(("usart", "TX"), quote!(crate::usart::TxPin)),
|
||||||
(("usart", "RX"), (quote!(crate::usart::RxPin), quote!())),
|
(("usart", "RX"), quote!(crate::usart::RxPin)),
|
||||||
(("usart", "CTS"), (quote!(crate::usart::CtsPin), quote!())),
|
(("usart", "CTS"), quote!(crate::usart::CtsPin)),
|
||||||
(("usart", "RTS"), (quote!(crate::usart::RtsPin), quote!())),
|
(("usart", "RTS"), quote!(crate::usart::RtsPin)),
|
||||||
(("usart", "CK"), (quote!(crate::usart::CkPin), quote!())),
|
(("usart", "CK"), quote!(crate::usart::CkPin)),
|
||||||
(("spi", "SCK"), (quote!(crate::spi::SckPin), quote!())),
|
(("spi", "SCK"), quote!(crate::spi::SckPin)),
|
||||||
(("spi", "MOSI"), (quote!(crate::spi::MosiPin), quote!())),
|
(("spi", "MOSI"), quote!(crate::spi::MosiPin)),
|
||||||
(("spi", "MISO"), (quote!(crate::spi::MisoPin), quote!())),
|
(("spi", "MISO"), quote!(crate::spi::MisoPin)),
|
||||||
(("i2c", "SDA"), (quote!(crate::i2c::SdaPin), quote!())),
|
(("i2c", "SDA"), quote!(crate::i2c::SdaPin)),
|
||||||
(("i2c", "SCL"), (quote!(crate::i2c::SclPin), quote!())),
|
(("i2c", "SCL"), quote!(crate::i2c::SclPin)),
|
||||||
(("rcc", "MCO_1"), (quote!(crate::rcc::McoPin), quote!())),
|
(("rcc", "MCO_1"), quote!(crate::rcc::McoPin)),
|
||||||
(("rcc", "MCO_2"), (quote!(crate::rcc::McoPin), quote!())),
|
(("rcc", "MCO_2"), quote!(crate::rcc::McoPin)),
|
||||||
(("dcmi", "D0"), (quote!(crate::dcmi::D0Pin), quote!())),
|
(("dcmi", "D0"), quote!(crate::dcmi::D0Pin)),
|
||||||
(("dcmi", "D1"), (quote!(crate::dcmi::D1Pin), quote!())),
|
(("dcmi", "D1"), quote!(crate::dcmi::D1Pin)),
|
||||||
(("dcmi", "D2"), (quote!(crate::dcmi::D2Pin), quote!())),
|
(("dcmi", "D2"), quote!(crate::dcmi::D2Pin)),
|
||||||
(("dcmi", "D3"), (quote!(crate::dcmi::D3Pin), quote!())),
|
(("dcmi", "D3"), quote!(crate::dcmi::D3Pin)),
|
||||||
(("dcmi", "D4"), (quote!(crate::dcmi::D4Pin), quote!())),
|
(("dcmi", "D4"), quote!(crate::dcmi::D4Pin)),
|
||||||
(("dcmi", "D5"), (quote!(crate::dcmi::D5Pin), quote!())),
|
(("dcmi", "D5"), quote!(crate::dcmi::D5Pin)),
|
||||||
(("dcmi", "D6"), (quote!(crate::dcmi::D6Pin), quote!())),
|
(("dcmi", "D6"), quote!(crate::dcmi::D6Pin)),
|
||||||
(("dcmi", "D7"), (quote!(crate::dcmi::D7Pin), quote!())),
|
(("dcmi", "D7"), quote!(crate::dcmi::D7Pin)),
|
||||||
(("dcmi", "D8"), (quote!(crate::dcmi::D8Pin), quote!())),
|
(("dcmi", "D8"), quote!(crate::dcmi::D8Pin)),
|
||||||
(("dcmi", "D9"), (quote!(crate::dcmi::D9Pin), quote!())),
|
(("dcmi", "D9"), quote!(crate::dcmi::D9Pin)),
|
||||||
(("dcmi", "D10"), (quote!(crate::dcmi::D10Pin), quote!())),
|
(("dcmi", "D10"), quote!(crate::dcmi::D10Pin)),
|
||||||
(("dcmi", "D11"), (quote!(crate::dcmi::D11Pin), quote!())),
|
(("dcmi", "D11"), quote!(crate::dcmi::D11Pin)),
|
||||||
(("dcmi", "D12"), (quote!(crate::dcmi::D12Pin), quote!())),
|
(("dcmi", "D12"), quote!(crate::dcmi::D12Pin)),
|
||||||
(("dcmi", "D13"), (quote!(crate::dcmi::D13Pin), quote!())),
|
(("dcmi", "D13"), quote!(crate::dcmi::D13Pin)),
|
||||||
(("dcmi", "HSYNC"), (quote!(crate::dcmi::HSyncPin), quote!())),
|
(("dcmi", "HSYNC"), quote!(crate::dcmi::HSyncPin)),
|
||||||
(("dcmi", "VSYNC"), (quote!(crate::dcmi::VSyncPin), quote!())),
|
(("dcmi", "VSYNC"), quote!(crate::dcmi::VSyncPin)),
|
||||||
(("dcmi", "PIXCLK"), (quote!(crate::dcmi::PixClkPin), quote!())),
|
(("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)),
|
||||||
(("otgfs", "DP"), (quote!(crate::usb_otg::DpPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otgfs", "DP"), quote!(crate::usb_otg::DpPin)),
|
||||||
(("otgfs", "DM"), (quote!(crate::usb_otg::DmPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otgfs", "DM"), quote!(crate::usb_otg::DmPin)),
|
||||||
(("otghs", "DP"), (quote!(crate::usb_otg::DpPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "DP"), quote!(crate::usb_otg::DpPin)),
|
||||||
(("otghs", "DM"), (quote!(crate::usb_otg::DmPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "DM"), quote!(crate::usb_otg::DmPin)),
|
||||||
(("otghs", "ULPI_CK"), (quote!(crate::usb_otg::UlpiClkPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_CK"), quote!(crate::usb_otg::UlpiClkPin)),
|
||||||
(("otghs", "ULPI_DIR"), (quote!(crate::usb_otg::UlpiDirPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_DIR"), quote!(crate::usb_otg::UlpiDirPin)),
|
||||||
(("otghs", "ULPI_NXT"), (quote!(crate::usb_otg::UlpiNxtPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_NXT"), quote!(crate::usb_otg::UlpiNxtPin)),
|
||||||
(("otghs", "ULPI_STP"), (quote!(crate::usb_otg::UlpiStpPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_STP"), quote!(crate::usb_otg::UlpiStpPin)),
|
||||||
(("otghs", "ULPI_D0"), (quote!(crate::usb_otg::UlpiD0Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D0"), quote!(crate::usb_otg::UlpiD0Pin)),
|
||||||
(("otghs", "ULPI_D1"), (quote!(crate::usb_otg::UlpiD1Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D1"), quote!(crate::usb_otg::UlpiD1Pin)),
|
||||||
(("otghs", "ULPI_D2"), (quote!(crate::usb_otg::UlpiD2Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D2"), quote!(crate::usb_otg::UlpiD2Pin)),
|
||||||
(("otghs", "ULPI_D3"), (quote!(crate::usb_otg::UlpiD3Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D3"), quote!(crate::usb_otg::UlpiD3Pin)),
|
||||||
(("otghs", "ULPI_D4"), (quote!(crate::usb_otg::UlpiD4Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D4"), quote!(crate::usb_otg::UlpiD4Pin)),
|
||||||
(("otghs", "ULPI_D5"), (quote!(crate::usb_otg::UlpiD5Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D5"), quote!(crate::usb_otg::UlpiD5Pin)),
|
||||||
(("otghs", "ULPI_D6"), (quote!(crate::usb_otg::UlpiD6Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D6"), quote!(crate::usb_otg::UlpiD6Pin)),
|
||||||
(("otghs", "ULPI_D7"), (quote!(crate::usb_otg::UlpiD7Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)),
|
||||||
(("can", "TX"), (quote!(crate::can::TxPin), quote!())),
|
(("can", "TX"), quote!(crate::can::TxPin)),
|
||||||
(("can", "RX"), (quote!(crate::can::RxPin), quote!())),
|
(("can", "RX"), quote!(crate::can::RxPin)),
|
||||||
(("eth", "REF_CLK"), (quote!(crate::eth::RefClkPin), quote!(#[cfg(feature="net")]))),
|
(("eth", "REF_CLK"), quote!(crate::eth::RefClkPin)),
|
||||||
(("eth", "MDIO"), (quote!(crate::eth::MDIOPin), quote!(#[cfg(feature="net")]))),
|
(("eth", "MDIO"), quote!(crate::eth::MDIOPin)),
|
||||||
(("eth", "MDC"), (quote!(crate::eth::MDCPin), quote!(#[cfg(feature="net")]))),
|
(("eth", "MDC"), quote!(crate::eth::MDCPin)),
|
||||||
(("eth", "CRS_DV"), (quote!(crate::eth::CRSPin), quote!(#[cfg(feature="net")]))),
|
(("eth", "CRS_DV"), quote!(crate::eth::CRSPin)),
|
||||||
(("eth", "RXD0"), (quote!(crate::eth::RXD0Pin), quote!(#[cfg(feature="net")]))),
|
(("eth", "RXD0"), quote!(crate::eth::RXD0Pin)),
|
||||||
(("eth", "RXD1"), (quote!(crate::eth::RXD1Pin), quote!(#[cfg(feature="net")]))),
|
(("eth", "RXD1"), quote!(crate::eth::RXD1Pin)),
|
||||||
(("eth", "TXD0"), (quote!(crate::eth::TXD0Pin), quote!(#[cfg(feature="net")]))),
|
(("eth", "TXD0"), quote!(crate::eth::TXD0Pin)),
|
||||||
(("eth", "TXD1"), (quote!(crate::eth::TXD1Pin), quote!(#[cfg(feature="net")]))),
|
(("eth", "TXD1"), quote!(crate::eth::TXD1Pin)),
|
||||||
(("eth", "TX_EN"), (quote!(crate::eth::TXEnPin), quote!(#[cfg(feature="net")]))),
|
(("eth", "TX_EN"), quote!(crate::eth::TXEnPin)),
|
||||||
(("fmc", "A0"), (quote!(crate::fmc::A0Pin), quote!())),
|
(("fmc", "A0"), quote!(crate::fmc::A0Pin)),
|
||||||
(("fmc", "A1"), (quote!(crate::fmc::A1Pin), quote!())),
|
(("fmc", "A1"), quote!(crate::fmc::A1Pin)),
|
||||||
(("fmc", "A2"), (quote!(crate::fmc::A2Pin), quote!())),
|
(("fmc", "A2"), quote!(crate::fmc::A2Pin)),
|
||||||
(("fmc", "A3"), (quote!(crate::fmc::A3Pin), quote!())),
|
(("fmc", "A3"), quote!(crate::fmc::A3Pin)),
|
||||||
(("fmc", "A4"), (quote!(crate::fmc::A4Pin), quote!())),
|
(("fmc", "A4"), quote!(crate::fmc::A4Pin)),
|
||||||
(("fmc", "A5"), (quote!(crate::fmc::A5Pin), quote!())),
|
(("fmc", "A5"), quote!(crate::fmc::A5Pin)),
|
||||||
(("fmc", "A6"), (quote!(crate::fmc::A6Pin), quote!())),
|
(("fmc", "A6"), quote!(crate::fmc::A6Pin)),
|
||||||
(("fmc", "A7"), (quote!(crate::fmc::A7Pin), quote!())),
|
(("fmc", "A7"), quote!(crate::fmc::A7Pin)),
|
||||||
(("fmc", "A8"), (quote!(crate::fmc::A8Pin), quote!())),
|
(("fmc", "A8"), quote!(crate::fmc::A8Pin)),
|
||||||
(("fmc", "A9"), (quote!(crate::fmc::A9Pin), quote!())),
|
(("fmc", "A9"), quote!(crate::fmc::A9Pin)),
|
||||||
(("fmc", "A10"), (quote!(crate::fmc::A10Pin), quote!())),
|
(("fmc", "A10"), quote!(crate::fmc::A10Pin)),
|
||||||
(("fmc", "A11"), (quote!(crate::fmc::A11Pin), quote!())),
|
(("fmc", "A11"), quote!(crate::fmc::A11Pin)),
|
||||||
(("fmc", "A12"), (quote!(crate::fmc::A12Pin), quote!())),
|
(("fmc", "A12"), quote!(crate::fmc::A12Pin)),
|
||||||
(("fmc", "A13"), (quote!(crate::fmc::A13Pin), quote!())),
|
(("fmc", "A13"), quote!(crate::fmc::A13Pin)),
|
||||||
(("fmc", "A14"), (quote!(crate::fmc::A14Pin), quote!())),
|
(("fmc", "A14"), quote!(crate::fmc::A14Pin)),
|
||||||
(("fmc", "A15"), (quote!(crate::fmc::A15Pin), quote!())),
|
(("fmc", "A15"), quote!(crate::fmc::A15Pin)),
|
||||||
(("fmc", "A16"), (quote!(crate::fmc::A16Pin), quote!())),
|
(("fmc", "A16"), quote!(crate::fmc::A16Pin)),
|
||||||
(("fmc", "A17"), (quote!(crate::fmc::A17Pin), quote!())),
|
(("fmc", "A17"), quote!(crate::fmc::A17Pin)),
|
||||||
(("fmc", "A18"), (quote!(crate::fmc::A18Pin), quote!())),
|
(("fmc", "A18"), quote!(crate::fmc::A18Pin)),
|
||||||
(("fmc", "A19"), (quote!(crate::fmc::A19Pin), quote!())),
|
(("fmc", "A19"), quote!(crate::fmc::A19Pin)),
|
||||||
(("fmc", "A20"), (quote!(crate::fmc::A20Pin), quote!())),
|
(("fmc", "A20"), quote!(crate::fmc::A20Pin)),
|
||||||
(("fmc", "A21"), (quote!(crate::fmc::A21Pin), quote!())),
|
(("fmc", "A21"), quote!(crate::fmc::A21Pin)),
|
||||||
(("fmc", "A22"), (quote!(crate::fmc::A22Pin), quote!())),
|
(("fmc", "A22"), quote!(crate::fmc::A22Pin)),
|
||||||
(("fmc", "A23"), (quote!(crate::fmc::A23Pin), quote!())),
|
(("fmc", "A23"), quote!(crate::fmc::A23Pin)),
|
||||||
(("fmc", "A24"), (quote!(crate::fmc::A24Pin), quote!())),
|
(("fmc", "A24"), quote!(crate::fmc::A24Pin)),
|
||||||
(("fmc", "A25"), (quote!(crate::fmc::A25Pin), quote!())),
|
(("fmc", "A25"), quote!(crate::fmc::A25Pin)),
|
||||||
(("fmc", "D0"), (quote!(crate::fmc::D0Pin), quote!())),
|
(("fmc", "D0"), quote!(crate::fmc::D0Pin)),
|
||||||
(("fmc", "D1"), (quote!(crate::fmc::D1Pin), quote!())),
|
(("fmc", "D1"), quote!(crate::fmc::D1Pin)),
|
||||||
(("fmc", "D2"), (quote!(crate::fmc::D2Pin), quote!())),
|
(("fmc", "D2"), quote!(crate::fmc::D2Pin)),
|
||||||
(("fmc", "D3"), (quote!(crate::fmc::D3Pin), quote!())),
|
(("fmc", "D3"), quote!(crate::fmc::D3Pin)),
|
||||||
(("fmc", "D4"), (quote!(crate::fmc::D4Pin), quote!())),
|
(("fmc", "D4"), quote!(crate::fmc::D4Pin)),
|
||||||
(("fmc", "D5"), (quote!(crate::fmc::D5Pin), quote!())),
|
(("fmc", "D5"), quote!(crate::fmc::D5Pin)),
|
||||||
(("fmc", "D6"), (quote!(crate::fmc::D6Pin), quote!())),
|
(("fmc", "D6"), quote!(crate::fmc::D6Pin)),
|
||||||
(("fmc", "D7"), (quote!(crate::fmc::D7Pin), quote!())),
|
(("fmc", "D7"), quote!(crate::fmc::D7Pin)),
|
||||||
(("fmc", "D8"), (quote!(crate::fmc::D8Pin), quote!())),
|
(("fmc", "D8"), quote!(crate::fmc::D8Pin)),
|
||||||
(("fmc", "D9"), (quote!(crate::fmc::D9Pin), quote!())),
|
(("fmc", "D9"), quote!(crate::fmc::D9Pin)),
|
||||||
(("fmc", "D10"), (quote!(crate::fmc::D10Pin), quote!())),
|
(("fmc", "D10"), quote!(crate::fmc::D10Pin)),
|
||||||
(("fmc", "D11"), (quote!(crate::fmc::D11Pin), quote!())),
|
(("fmc", "D11"), quote!(crate::fmc::D11Pin)),
|
||||||
(("fmc", "D12"), (quote!(crate::fmc::D12Pin), quote!())),
|
(("fmc", "D12"), quote!(crate::fmc::D12Pin)),
|
||||||
(("fmc", "D13"), (quote!(crate::fmc::D13Pin), quote!())),
|
(("fmc", "D13"), quote!(crate::fmc::D13Pin)),
|
||||||
(("fmc", "D14"), (quote!(crate::fmc::D14Pin), quote!())),
|
(("fmc", "D14"), quote!(crate::fmc::D14Pin)),
|
||||||
(("fmc", "D15"), (quote!(crate::fmc::D15Pin), quote!())),
|
(("fmc", "D15"), quote!(crate::fmc::D15Pin)),
|
||||||
(("fmc", "D16"), (quote!(crate::fmc::D16Pin), quote!())),
|
(("fmc", "D16"), quote!(crate::fmc::D16Pin)),
|
||||||
(("fmc", "D17"), (quote!(crate::fmc::D17Pin), quote!())),
|
(("fmc", "D17"), quote!(crate::fmc::D17Pin)),
|
||||||
(("fmc", "D18"), (quote!(crate::fmc::D18Pin), quote!())),
|
(("fmc", "D18"), quote!(crate::fmc::D18Pin)),
|
||||||
(("fmc", "D19"), (quote!(crate::fmc::D19Pin), quote!())),
|
(("fmc", "D19"), quote!(crate::fmc::D19Pin)),
|
||||||
(("fmc", "D20"), (quote!(crate::fmc::D20Pin), quote!())),
|
(("fmc", "D20"), quote!(crate::fmc::D20Pin)),
|
||||||
(("fmc", "D21"), (quote!(crate::fmc::D21Pin), quote!())),
|
(("fmc", "D21"), quote!(crate::fmc::D21Pin)),
|
||||||
(("fmc", "D22"), (quote!(crate::fmc::D22Pin), quote!())),
|
(("fmc", "D22"), quote!(crate::fmc::D22Pin)),
|
||||||
(("fmc", "D23"), (quote!(crate::fmc::D23Pin), quote!())),
|
(("fmc", "D23"), quote!(crate::fmc::D23Pin)),
|
||||||
(("fmc", "D24"), (quote!(crate::fmc::D24Pin), quote!())),
|
(("fmc", "D24"), quote!(crate::fmc::D24Pin)),
|
||||||
(("fmc", "D25"), (quote!(crate::fmc::D25Pin), quote!())),
|
(("fmc", "D25"), quote!(crate::fmc::D25Pin)),
|
||||||
(("fmc", "D26"), (quote!(crate::fmc::D26Pin), quote!())),
|
(("fmc", "D26"), quote!(crate::fmc::D26Pin)),
|
||||||
(("fmc", "D27"), (quote!(crate::fmc::D27Pin), quote!())),
|
(("fmc", "D27"), quote!(crate::fmc::D27Pin)),
|
||||||
(("fmc", "D28"), (quote!(crate::fmc::D28Pin), quote!())),
|
(("fmc", "D28"), quote!(crate::fmc::D28Pin)),
|
||||||
(("fmc", "D29"), (quote!(crate::fmc::D29Pin), quote!())),
|
(("fmc", "D29"), quote!(crate::fmc::D29Pin)),
|
||||||
(("fmc", "D30"), (quote!(crate::fmc::D30Pin), quote!())),
|
(("fmc", "D30"), quote!(crate::fmc::D30Pin)),
|
||||||
(("fmc", "D31"), (quote!(crate::fmc::D31Pin), quote!())),
|
(("fmc", "D31"), quote!(crate::fmc::D31Pin)),
|
||||||
(("fmc", "DA0"), (quote!(crate::fmc::DA0Pin), quote!())),
|
(("fmc", "DA0"), quote!(crate::fmc::DA0Pin)),
|
||||||
(("fmc", "DA1"), (quote!(crate::fmc::DA1Pin), quote!())),
|
(("fmc", "DA1"), quote!(crate::fmc::DA1Pin)),
|
||||||
(("fmc", "DA2"), (quote!(crate::fmc::DA2Pin), quote!())),
|
(("fmc", "DA2"), quote!(crate::fmc::DA2Pin)),
|
||||||
(("fmc", "DA3"), (quote!(crate::fmc::DA3Pin), quote!())),
|
(("fmc", "DA3"), quote!(crate::fmc::DA3Pin)),
|
||||||
(("fmc", "DA4"), (quote!(crate::fmc::DA4Pin), quote!())),
|
(("fmc", "DA4"), quote!(crate::fmc::DA4Pin)),
|
||||||
(("fmc", "DA5"), (quote!(crate::fmc::DA5Pin), quote!())),
|
(("fmc", "DA5"), quote!(crate::fmc::DA5Pin)),
|
||||||
(("fmc", "DA6"), (quote!(crate::fmc::DA6Pin), quote!())),
|
(("fmc", "DA6"), quote!(crate::fmc::DA6Pin)),
|
||||||
(("fmc", "DA7"), (quote!(crate::fmc::DA7Pin), quote!())),
|
(("fmc", "DA7"), quote!(crate::fmc::DA7Pin)),
|
||||||
(("fmc", "DA8"), (quote!(crate::fmc::DA8Pin), quote!())),
|
(("fmc", "DA8"), quote!(crate::fmc::DA8Pin)),
|
||||||
(("fmc", "DA9"), (quote!(crate::fmc::DA9Pin), quote!())),
|
(("fmc", "DA9"), quote!(crate::fmc::DA9Pin)),
|
||||||
(("fmc", "DA10"), (quote!(crate::fmc::DA10Pin), quote!())),
|
(("fmc", "DA10"), quote!(crate::fmc::DA10Pin)),
|
||||||
(("fmc", "DA11"), (quote!(crate::fmc::DA11Pin), quote!())),
|
(("fmc", "DA11"), quote!(crate::fmc::DA11Pin)),
|
||||||
(("fmc", "DA12"), (quote!(crate::fmc::DA12Pin), quote!())),
|
(("fmc", "DA12"), quote!(crate::fmc::DA12Pin)),
|
||||||
(("fmc", "DA13"), (quote!(crate::fmc::DA13Pin), quote!())),
|
(("fmc", "DA13"), quote!(crate::fmc::DA13Pin)),
|
||||||
(("fmc", "DA14"), (quote!(crate::fmc::DA14Pin), quote!())),
|
(("fmc", "DA14"), quote!(crate::fmc::DA14Pin)),
|
||||||
(("fmc", "DA15"), (quote!(crate::fmc::DA15Pin), quote!())),
|
(("fmc", "DA15"), quote!(crate::fmc::DA15Pin)),
|
||||||
(("fmc", "SDNWE"), (quote!(crate::fmc::SDNWEPin), quote!())),
|
(("fmc", "SDNWE"), quote!(crate::fmc::SDNWEPin)),
|
||||||
(("fmc", "SDNCAS"), (quote!(crate::fmc::SDNCASPin), quote!())),
|
(("fmc", "SDNCAS"), quote!(crate::fmc::SDNCASPin)),
|
||||||
(("fmc", "SDNRAS"), (quote!(crate::fmc::SDNRASPin), quote!())),
|
(("fmc", "SDNRAS"), quote!(crate::fmc::SDNRASPin)),
|
||||||
(("fmc", "SDNE0"), (quote!(crate::fmc::SDNE0Pin), quote!())),
|
(("fmc", "SDNE0"), quote!(crate::fmc::SDNE0Pin)),
|
||||||
(("fmc", "SDNE1"), (quote!(crate::fmc::SDNE1Pin), quote!())),
|
(("fmc", "SDNE1"), quote!(crate::fmc::SDNE1Pin)),
|
||||||
(("fmc", "SDCKE0"), (quote!(crate::fmc::SDCKE0Pin), quote!())),
|
(("fmc", "SDCKE0"), quote!(crate::fmc::SDCKE0Pin)),
|
||||||
(("fmc", "SDCKE1"), (quote!(crate::fmc::SDCKE1Pin), quote!())),
|
(("fmc", "SDCKE1"), quote!(crate::fmc::SDCKE1Pin)),
|
||||||
(("fmc", "SDCLK"), (quote!(crate::fmc::SDCLKPin), quote!())),
|
(("fmc", "SDCLK"), quote!(crate::fmc::SDCLKPin)),
|
||||||
(("fmc", "NBL0"), (quote!(crate::fmc::NBL0Pin), quote!())),
|
(("fmc", "NBL0"), quote!(crate::fmc::NBL0Pin)),
|
||||||
(("fmc", "NBL1"), (quote!(crate::fmc::NBL1Pin), quote!())),
|
(("fmc", "NBL1"), quote!(crate::fmc::NBL1Pin)),
|
||||||
(("fmc", "NBL2"), (quote!(crate::fmc::NBL2Pin), quote!())),
|
(("fmc", "NBL2"), quote!(crate::fmc::NBL2Pin)),
|
||||||
(("fmc", "NBL3"), (quote!(crate::fmc::NBL3Pin), quote!())),
|
(("fmc", "NBL3"), quote!(crate::fmc::NBL3Pin)),
|
||||||
(("fmc", "INT"), (quote!(crate::fmc::INTPin), quote!())),
|
(("fmc", "INT"), quote!(crate::fmc::INTPin)),
|
||||||
(("fmc", "NL"), (quote!(crate::fmc::NLPin), quote!())),
|
(("fmc", "NL"), quote!(crate::fmc::NLPin)),
|
||||||
(("fmc", "NWAIT"), (quote!(crate::fmc::NWaitPin), quote!())),
|
(("fmc", "NWAIT"), quote!(crate::fmc::NWaitPin)),
|
||||||
(("fmc", "NE1"), (quote!(crate::fmc::NE1Pin), quote!())),
|
(("fmc", "NE1"), quote!(crate::fmc::NE1Pin)),
|
||||||
(("fmc", "NE2"), (quote!(crate::fmc::NE2Pin), quote!())),
|
(("fmc", "NE2"), quote!(crate::fmc::NE2Pin)),
|
||||||
(("fmc", "NE3"), (quote!(crate::fmc::NE3Pin), quote!())),
|
(("fmc", "NE3"), quote!(crate::fmc::NE3Pin)),
|
||||||
(("fmc", "NE4"), (quote!(crate::fmc::NE4Pin), quote!())),
|
(("fmc", "NE4"), quote!(crate::fmc::NE4Pin)),
|
||||||
(("fmc", "NCE"), (quote!(crate::fmc::NCEPin), quote!())),
|
(("fmc", "NCE"), quote!(crate::fmc::NCEPin)),
|
||||||
(("fmc", "NOE"), (quote!(crate::fmc::NOEPin), quote!())),
|
(("fmc", "NOE"), quote!(crate::fmc::NOEPin)),
|
||||||
(("fmc", "NWE"), (quote!(crate::fmc::NWEPin), quote!())),
|
(("fmc", "NWE"), quote!(crate::fmc::NWEPin)),
|
||||||
(("fmc", "Clk"), (quote!(crate::fmc::ClkPin), quote!())),
|
(("fmc", "Clk"), quote!(crate::fmc::ClkPin)),
|
||||||
(("fmc", "BA0"), (quote!(crate::fmc::BA0Pin), quote!())),
|
(("fmc", "BA0"), quote!(crate::fmc::BA0Pin)),
|
||||||
(("fmc", "BA1"), (quote!(crate::fmc::BA1Pin), quote!())),
|
(("fmc", "BA1"), quote!(crate::fmc::BA1Pin)),
|
||||||
(("timer", "CH1"), (quote!(crate::pwm::Channel1Pin), quote!())),
|
(("timer", "CH1"), quote!(crate::pwm::Channel1Pin)),
|
||||||
(("timer", "CH1N"), (quote!(crate::pwm::Channel1ComplementaryPin), quote!())),
|
(("timer", "CH1N"), quote!(crate::pwm::Channel1ComplementaryPin)),
|
||||||
(("timer", "CH2"), (quote!(crate::pwm::Channel2Pin), quote!())),
|
(("timer", "CH2"), quote!(crate::pwm::Channel2Pin)),
|
||||||
(("timer", "CH2N"), (quote!(crate::pwm::Channel2ComplementaryPin), quote!())),
|
(("timer", "CH2N"), quote!(crate::pwm::Channel2ComplementaryPin)),
|
||||||
(("timer", "CH3"), (quote!(crate::pwm::Channel3Pin), quote!())),
|
(("timer", "CH3"), quote!(crate::pwm::Channel3Pin)),
|
||||||
(("timer", "CH3N"), (quote!(crate::pwm::Channel3ComplementaryPin), quote!())),
|
(("timer", "CH3N"), quote!(crate::pwm::Channel3ComplementaryPin)),
|
||||||
(("timer", "CH4"), (quote!(crate::pwm::Channel4Pin), quote!())),
|
(("timer", "CH4"), quote!(crate::pwm::Channel4Pin)),
|
||||||
(("timer", "CH4N"), (quote!(crate::pwm::Channel4ComplementaryPin), quote!())),
|
(("timer", "CH4N"), quote!(crate::pwm::Channel4ComplementaryPin)),
|
||||||
(("timer", "ETR"), (quote!(crate::pwm::ExternalTriggerPin), quote!())),
|
(("timer", "ETR"), quote!(crate::pwm::ExternalTriggerPin)),
|
||||||
(("timer", "BKIN"), (quote!(crate::pwm::BreakInputPin), quote!())),
|
(("timer", "BKIN"), quote!(crate::pwm::BreakInputPin)),
|
||||||
(("timer", "BKIN_COMP1"), (quote!(crate::pwm::BreakInputComparator1Pin), quote!())),
|
(("timer", "BKIN_COMP1"), quote!(crate::pwm::BreakInputComparator1Pin)),
|
||||||
(("timer", "BKIN_COMP2"), (quote!(crate::pwm::BreakInputComparator2Pin), quote!())),
|
(("timer", "BKIN_COMP2"), quote!(crate::pwm::BreakInputComparator2Pin)),
|
||||||
(("timer", "BKIN2"), (quote!(crate::pwm::BreakInput2Pin), quote!())),
|
(("timer", "BKIN2"), quote!(crate::pwm::BreakInput2Pin)),
|
||||||
(("timer", "BKIN2_COMP1"), (quote!(crate::pwm::BreakInput2Comparator1Pin), quote!())),
|
(("timer", "BKIN2_COMP1"), quote!(crate::pwm::BreakInput2Comparator1Pin)),
|
||||||
(("timer", "BKIN2_COMP2"), (quote!(crate::pwm::BreakInput2Comparator2Pin), quote!())),
|
(("timer", "BKIN2_COMP2"), quote!(crate::pwm::BreakInput2Comparator2Pin)),
|
||||||
(("sdmmc", "CK"), (quote!(crate::sdmmc::CkPin), quote!())),
|
(("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)),
|
||||||
(("sdmmc", "CMD"), (quote!(crate::sdmmc::CmdPin), quote!())),
|
(("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)),
|
||||||
(("sdmmc", "D0"), (quote!(crate::sdmmc::D0Pin), quote!())),
|
(("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)),
|
||||||
(("sdmmc", "D1"), (quote!(crate::sdmmc::D1Pin), quote!())),
|
(("sdmmc", "D1"), quote!(crate::sdmmc::D1Pin)),
|
||||||
(("sdmmc", "D2"), (quote!(crate::sdmmc::D2Pin), quote!())),
|
(("sdmmc", "D2"), quote!(crate::sdmmc::D2Pin)),
|
||||||
(("sdmmc", "D3"), (quote!(crate::sdmmc::D3Pin), quote!())),
|
(("sdmmc", "D3"), quote!(crate::sdmmc::D3Pin)),
|
||||||
(("sdmmc", "D4"), (quote!(crate::sdmmc::D4Pin), quote!())),
|
(("sdmmc", "D4"), quote!(crate::sdmmc::D4Pin)),
|
||||||
(("sdmmc", "D5"), (quote!(crate::sdmmc::D5Pin), quote!())),
|
(("sdmmc", "D5"), quote!(crate::sdmmc::D5Pin)),
|
||||||
(("sdmmc", "D6"), (quote!(crate::sdmmc::D6Pin), quote!())),
|
(("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)),
|
||||||
(("sdmmc", "D6"), (quote!(crate::sdmmc::D7Pin), quote!())),
|
(("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)),
|
||||||
(("sdmmc", "D8"), (quote!(crate::sdmmc::D8Pin), quote!())),
|
(("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)),
|
||||||
].into();
|
].into();
|
||||||
|
|
||||||
for p in METADATA.peripherals {
|
for p in METADATA.peripherals {
|
||||||
if let Some(regs) = &p.registers {
|
if let Some(regs) = &p.registers {
|
||||||
for pin in p.pins {
|
for pin in p.pins {
|
||||||
let key = (regs.kind, pin.signal);
|
let key = (regs.kind, pin.signal);
|
||||||
if let Some((tr, cfgs)) = signals.get(&key) {
|
if let Some(tr) = signals.get(&key) {
|
||||||
let mut peri = format_ident!("{}", p.name);
|
let mut peri = format_ident!("{}", p.name);
|
||||||
let pin_name = format_ident!("{}", pin.pin);
|
let pin_name = format_ident!("{}", pin.pin);
|
||||||
let af = pin.af.unwrap_or(0);
|
let af = pin.af.unwrap_or(0);
|
||||||
@ -453,7 +453,6 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g.extend(quote! {
|
g.extend(quote! {
|
||||||
#cfgs
|
|
||||||
pin_trait_impl!(#tr, #peri, #pin_name, #af);
|
pin_trait_impl!(#tr, #peri, #pin_name, #af);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
|
#[cfg(feature = "net")]
|
||||||
#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")]
|
#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")]
|
||||||
#[cfg_attr(eth_v2, path = "v2/mod.rs")]
|
#[cfg_attr(eth_v2, path = "v2/mod.rs")]
|
||||||
mod _version;
|
mod _version;
|
||||||
pub mod generic_smi;
|
pub mod generic_smi;
|
||||||
|
|
||||||
|
#[cfg(feature = "net")]
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
|
|
||||||
/// Station Management Interface (SMI) on an ethernet PHY
|
/// Station Management Interface (SMI) on an ethernet PHY
|
||||||
|
104
embassy-stm32/src/flash/f3.rs
Normal file
104
embassy-stm32/src/flash/f3.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
use core::convert::TryInto;
|
||||||
|
use core::ptr::write_volatile;
|
||||||
|
|
||||||
|
use crate::flash::Error;
|
||||||
|
use crate::pac;
|
||||||
|
|
||||||
|
pub(crate) unsafe fn lock() {
|
||||||
|
pac::FLASH.cr().modify(|w| w.set_lock(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn unlock() {
|
||||||
|
pac::FLASH.keyr().write(|w| w.set_fkeyr(0x4567_0123));
|
||||||
|
pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
|
||||||
|
pac::FLASH.cr().write(|w| w.set_pg(true));
|
||||||
|
|
||||||
|
let ret = {
|
||||||
|
let mut ret: Result<(), Error> = Ok(());
|
||||||
|
let mut offset = offset;
|
||||||
|
for chunk in buf.chunks(2) {
|
||||||
|
write_volatile(
|
||||||
|
offset as *mut u16,
|
||||||
|
u16::from_le_bytes(chunk[0..2].try_into().unwrap()),
|
||||||
|
);
|
||||||
|
offset += chunk.len() as u32;
|
||||||
|
|
||||||
|
ret = blocking_wait_ready();
|
||||||
|
if ret.is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
};
|
||||||
|
|
||||||
|
pac::FLASH.cr().write(|w| w.set_pg(false));
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
|
||||||
|
for page in (from..to).step_by(super::ERASE_SIZE) {
|
||||||
|
pac::FLASH.cr().modify(|w| {
|
||||||
|
w.set_per(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
pac::FLASH.ar().write(|w| w.set_far(page));
|
||||||
|
|
||||||
|
pac::FLASH.cr().modify(|w| {
|
||||||
|
w.set_strt(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut ret: Result<(), Error> = blocking_wait_ready();
|
||||||
|
|
||||||
|
if !pac::FLASH.sr().read().eop() {
|
||||||
|
trace!("FLASH: EOP not set");
|
||||||
|
ret = Err(Error::Prog);
|
||||||
|
} else {
|
||||||
|
pac::FLASH.sr().write(|w| w.set_eop(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
pac::FLASH.cr().modify(|w| w.set_per(false));
|
||||||
|
|
||||||
|
clear_all_err();
|
||||||
|
if ret.is_err() {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn clear_all_err() {
|
||||||
|
pac::FLASH.sr().modify(|w| {
|
||||||
|
if w.pgerr() {
|
||||||
|
w.set_pgerr(true);
|
||||||
|
}
|
||||||
|
if w.wrprterr() {
|
||||||
|
w.set_wrprterr(true);
|
||||||
|
}
|
||||||
|
if w.eop() {
|
||||||
|
w.set_eop(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
|
||||||
|
loop {
|
||||||
|
let sr = pac::FLASH.sr().read();
|
||||||
|
|
||||||
|
if !sr.bsy() {
|
||||||
|
if sr.wrprterr() {
|
||||||
|
return Err(Error::Protected);
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.pgerr() {
|
||||||
|
return Err(Error::Seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
138
embassy-stm32/src/flash/f7.rs
Normal file
138
embassy-stm32/src/flash/f7.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use core::convert::TryInto;
|
||||||
|
use core::ptr::write_volatile;
|
||||||
|
|
||||||
|
use atomic_polyfill::{fence, Ordering};
|
||||||
|
|
||||||
|
use crate::flash::Error;
|
||||||
|
use crate::pac;
|
||||||
|
|
||||||
|
pub(crate) unsafe fn lock() {
|
||||||
|
pac::FLASH.cr().modify(|w| w.set_lock(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn unlock() {
|
||||||
|
pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123));
|
||||||
|
pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
|
||||||
|
pac::FLASH.cr().write(|w| {
|
||||||
|
w.set_pg(true);
|
||||||
|
w.set_psize(pac::flash::vals::Psize::PSIZE32);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ret = {
|
||||||
|
let mut ret: Result<(), Error> = Ok(());
|
||||||
|
let mut offset = offset;
|
||||||
|
for chunk in buf.chunks(super::WRITE_SIZE) {
|
||||||
|
for val in chunk.chunks(4) {
|
||||||
|
write_volatile(
|
||||||
|
offset as *mut u32,
|
||||||
|
u32::from_le_bytes(val[0..4].try_into().unwrap()),
|
||||||
|
);
|
||||||
|
offset += val.len() as u32;
|
||||||
|
|
||||||
|
// prevents parallelism errors
|
||||||
|
fence(Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = blocking_wait_ready();
|
||||||
|
if ret.is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
};
|
||||||
|
|
||||||
|
pac::FLASH.cr().write(|w| w.set_pg(false));
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
|
||||||
|
let start_sector = if from >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 {
|
||||||
|
4 + (from - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32
|
||||||
|
} else {
|
||||||
|
(from - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8)
|
||||||
|
};
|
||||||
|
|
||||||
|
let end_sector = if to >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 {
|
||||||
|
4 + (to - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32
|
||||||
|
} else {
|
||||||
|
(to - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8)
|
||||||
|
};
|
||||||
|
|
||||||
|
for sector in start_sector..end_sector {
|
||||||
|
let ret = erase_sector(sector as u8);
|
||||||
|
if ret.is_err() {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn erase_sector(sector: u8) -> Result<(), Error> {
|
||||||
|
pac::FLASH.cr().modify(|w| {
|
||||||
|
w.set_ser(true);
|
||||||
|
w.set_snb(sector)
|
||||||
|
});
|
||||||
|
|
||||||
|
pac::FLASH.cr().modify(|w| {
|
||||||
|
w.set_strt(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ret: Result<(), Error> = blocking_wait_ready();
|
||||||
|
|
||||||
|
pac::FLASH.cr().modify(|w| w.set_ser(false));
|
||||||
|
|
||||||
|
clear_all_err();
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn clear_all_err() {
|
||||||
|
pac::FLASH.sr().modify(|w| {
|
||||||
|
if w.erserr() {
|
||||||
|
w.set_erserr(true);
|
||||||
|
}
|
||||||
|
if w.pgperr() {
|
||||||
|
w.set_pgperr(true);
|
||||||
|
}
|
||||||
|
if w.pgaerr() {
|
||||||
|
w.set_pgaerr(true);
|
||||||
|
}
|
||||||
|
if w.wrperr() {
|
||||||
|
w.set_wrperr(true);
|
||||||
|
}
|
||||||
|
if w.eop() {
|
||||||
|
w.set_eop(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
|
||||||
|
loop {
|
||||||
|
let sr = pac::FLASH.sr().read();
|
||||||
|
|
||||||
|
if !sr.bsy() {
|
||||||
|
if sr.erserr() {
|
||||||
|
return Err(Error::Seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.pgperr() {
|
||||||
|
return Err(Error::Parallelism);
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.pgaerr() {
|
||||||
|
return Err(Error::Unaligned);
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.wrperr() {
|
||||||
|
return Err(Error::Protected);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
202
embassy-stm32/src/flash/h7.rs
Normal file
202
embassy-stm32/src/flash/h7.rs
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
use core::convert::TryInto;
|
||||||
|
use core::ptr::write_volatile;
|
||||||
|
|
||||||
|
use crate::flash::Error;
|
||||||
|
use crate::pac;
|
||||||
|
|
||||||
|
const SECOND_BANK_OFFSET: usize = 0x0010_0000;
|
||||||
|
|
||||||
|
const fn is_dual_bank() -> bool {
|
||||||
|
super::FLASH_SIZE / 2 > super::ERASE_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn lock() {
|
||||||
|
pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true));
|
||||||
|
if is_dual_bank() {
|
||||||
|
pac::FLASH.bank(1).cr().modify(|w| w.set_lock(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn unlock() {
|
||||||
|
pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||||
|
pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||||
|
|
||||||
|
if is_dual_bank() {
|
||||||
|
pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||||
|
pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
|
||||||
|
let bank = if !is_dual_bank() || (offset - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32
|
||||||
|
{
|
||||||
|
pac::FLASH.bank(0)
|
||||||
|
} else {
|
||||||
|
pac::FLASH.bank(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
bank.cr().write(|w| {
|
||||||
|
w.set_pg(true);
|
||||||
|
w.set_psize(2); // 32 bits at once
|
||||||
|
});
|
||||||
|
|
||||||
|
let ret = {
|
||||||
|
let mut ret: Result<(), Error> = Ok(());
|
||||||
|
let mut offset = offset;
|
||||||
|
'outer: for chunk in buf.chunks(super::WRITE_SIZE) {
|
||||||
|
for val in chunk.chunks(4) {
|
||||||
|
trace!("Writing at {:x}", offset);
|
||||||
|
write_volatile(
|
||||||
|
offset as *mut u32,
|
||||||
|
u32::from_le_bytes(val[0..4].try_into().unwrap()),
|
||||||
|
);
|
||||||
|
offset += val.len() as u32;
|
||||||
|
|
||||||
|
ret = blocking_wait_ready(bank);
|
||||||
|
bank.sr().modify(|w| {
|
||||||
|
if w.eop() {
|
||||||
|
w.set_eop(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if ret.is_err() {
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
};
|
||||||
|
|
||||||
|
bank.cr().write(|w| w.set_pg(false));
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
|
||||||
|
let from = from - super::FLASH_BASE as u32;
|
||||||
|
let to = to - super::FLASH_BASE as u32;
|
||||||
|
|
||||||
|
let bank_size = (super::FLASH_SIZE / 2) as u32;
|
||||||
|
|
||||||
|
let (bank, start, end) = if to <= bank_size {
|
||||||
|
let start_sector = from / super::ERASE_SIZE as u32;
|
||||||
|
let end_sector = to / super::ERASE_SIZE as u32;
|
||||||
|
(0, start_sector, end_sector)
|
||||||
|
} else if from >= SECOND_BANK_OFFSET as u32 && to <= (SECOND_BANK_OFFSET as u32 + bank_size) {
|
||||||
|
let start_sector = (from - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32;
|
||||||
|
let end_sector = (to - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32;
|
||||||
|
(1, start_sector, end_sector)
|
||||||
|
} else {
|
||||||
|
error!("Attempting to write outside of defined sectors");
|
||||||
|
return Err(Error::Unaligned);
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("Erasing bank {}, sectors from {} to {}", bank, start, end);
|
||||||
|
for sector in start..end {
|
||||||
|
let ret = erase_sector(pac::FLASH.bank(bank), sector as u8);
|
||||||
|
if ret.is_err() {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn erase_sector(bank: pac::flash::Bank, sector: u8) -> Result<(), Error> {
|
||||||
|
bank.cr().modify(|w| {
|
||||||
|
w.set_ser(true);
|
||||||
|
w.set_snb(sector)
|
||||||
|
});
|
||||||
|
|
||||||
|
bank.cr().modify(|w| {
|
||||||
|
w.set_start(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ret: Result<(), Error> = blocking_wait_ready(bank);
|
||||||
|
|
||||||
|
bank.cr().modify(|w| w.set_ser(false));
|
||||||
|
|
||||||
|
bank_clear_all_err(bank);
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn clear_all_err() {
|
||||||
|
bank_clear_all_err(pac::FLASH.bank(0));
|
||||||
|
bank_clear_all_err(pac::FLASH.bank(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn bank_clear_all_err(bank: pac::flash::Bank) {
|
||||||
|
bank.sr().modify(|w| {
|
||||||
|
if w.wrperr() {
|
||||||
|
w.set_wrperr(true);
|
||||||
|
}
|
||||||
|
if w.pgserr() {
|
||||||
|
w.set_pgserr(true);
|
||||||
|
}
|
||||||
|
if w.strberr() {
|
||||||
|
// single address was written multiple times, can be ignored
|
||||||
|
w.set_strberr(true);
|
||||||
|
}
|
||||||
|
if w.incerr() {
|
||||||
|
// writing to a different address when programming 256 bit word was not finished
|
||||||
|
w.set_incerr(true);
|
||||||
|
}
|
||||||
|
if w.operr() {
|
||||||
|
w.set_operr(true);
|
||||||
|
}
|
||||||
|
if w.sneccerr1() {
|
||||||
|
// single ECC error
|
||||||
|
w.set_sneccerr1(true);
|
||||||
|
}
|
||||||
|
if w.dbeccerr() {
|
||||||
|
// double ECC error
|
||||||
|
w.set_dbeccerr(true);
|
||||||
|
}
|
||||||
|
if w.rdperr() {
|
||||||
|
w.set_rdperr(true);
|
||||||
|
}
|
||||||
|
if w.rdserr() {
|
||||||
|
w.set_rdserr(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
|
||||||
|
loop {
|
||||||
|
let sr = bank.sr().read();
|
||||||
|
|
||||||
|
if !sr.bsy() && !sr.qw() {
|
||||||
|
if sr.wrperr() {
|
||||||
|
return Err(Error::Protected);
|
||||||
|
}
|
||||||
|
if sr.pgserr() {
|
||||||
|
error!("pgserr");
|
||||||
|
return Err(Error::Seq);
|
||||||
|
}
|
||||||
|
if sr.incerr() {
|
||||||
|
// writing to a different address when programming 256 bit word was not finished
|
||||||
|
error!("incerr");
|
||||||
|
return Err(Error::Seq);
|
||||||
|
}
|
||||||
|
if sr.operr() {
|
||||||
|
return Err(Error::Prog);
|
||||||
|
}
|
||||||
|
if sr.sneccerr1() {
|
||||||
|
// single ECC error
|
||||||
|
return Err(Error::Prog);
|
||||||
|
}
|
||||||
|
if sr.dbeccerr() {
|
||||||
|
// double ECC error
|
||||||
|
return Err(Error::Prog);
|
||||||
|
}
|
||||||
|
if sr.rdperr() {
|
||||||
|
return Err(Error::Protected);
|
||||||
|
}
|
||||||
|
if sr.rdserr() {
|
||||||
|
return Err(Error::Protected);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
185
embassy-stm32/src/flash/l.rs
Normal file
185
embassy-stm32/src/flash/l.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
use core::convert::TryInto;
|
||||||
|
use core::ptr::write_volatile;
|
||||||
|
|
||||||
|
use crate::flash::Error;
|
||||||
|
use crate::pac;
|
||||||
|
|
||||||
|
pub(crate) unsafe fn lock() {
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
pac::FLASH.cr().modify(|w| w.set_lock(true));
|
||||||
|
|
||||||
|
#[cfg(any(flash_l0))]
|
||||||
|
pac::FLASH.pecr().modify(|w| {
|
||||||
|
w.set_optlock(true);
|
||||||
|
w.set_prglock(true);
|
||||||
|
w.set_pelock(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn unlock() {
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
{
|
||||||
|
pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
|
||||||
|
pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(flash_l0, flash_l1))]
|
||||||
|
{
|
||||||
|
pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x89ABCDEF));
|
||||||
|
pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x02030405));
|
||||||
|
|
||||||
|
pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x8C9DAEBF));
|
||||||
|
pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x13141516));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
pac::FLASH.cr().write(|w| w.set_pg(true));
|
||||||
|
|
||||||
|
let ret = {
|
||||||
|
let mut ret: Result<(), Error> = Ok(());
|
||||||
|
let mut offset = offset;
|
||||||
|
for chunk in buf.chunks(super::WRITE_SIZE) {
|
||||||
|
for val in chunk.chunks(4) {
|
||||||
|
write_volatile(
|
||||||
|
offset as *mut u32,
|
||||||
|
u32::from_le_bytes(val[0..4].try_into().unwrap()),
|
||||||
|
);
|
||||||
|
offset += val.len() as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = blocking_wait_ready();
|
||||||
|
if ret.is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
pac::FLASH.cr().write(|w| w.set_pg(false));
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
|
||||||
|
for page in (from..to).step_by(super::ERASE_SIZE) {
|
||||||
|
#[cfg(any(flash_l0, flash_l1))]
|
||||||
|
{
|
||||||
|
pac::FLASH.pecr().modify(|w| {
|
||||||
|
w.set_erase(true);
|
||||||
|
w.set_prog(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
write_volatile(page as *mut u32, 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
{
|
||||||
|
let idx = page / super::ERASE_SIZE as u32;
|
||||||
|
|
||||||
|
pac::FLASH.cr().modify(|w| {
|
||||||
|
w.set_per(true);
|
||||||
|
w.set_pnb(idx as u8);
|
||||||
|
#[cfg(any(flash_wl, flash_wb))]
|
||||||
|
w.set_strt(true);
|
||||||
|
#[cfg(any(flash_l4))]
|
||||||
|
w.set_start(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret: Result<(), Error> = blocking_wait_ready();
|
||||||
|
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
pac::FLASH.cr().modify(|w| w.set_per(false));
|
||||||
|
|
||||||
|
#[cfg(any(flash_l0, flash_l1))]
|
||||||
|
pac::FLASH.pecr().modify(|w| {
|
||||||
|
w.set_erase(false);
|
||||||
|
w.set_prog(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
clear_all_err();
|
||||||
|
if ret.is_err() {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn clear_all_err() {
|
||||||
|
pac::FLASH.sr().modify(|w| {
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4, flash_l0))]
|
||||||
|
if w.rderr() {
|
||||||
|
w.set_rderr(true);
|
||||||
|
}
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
if w.fasterr() {
|
||||||
|
w.set_fasterr(true);
|
||||||
|
}
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
if w.miserr() {
|
||||||
|
w.set_miserr(true);
|
||||||
|
}
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
if w.pgserr() {
|
||||||
|
w.set_pgserr(true);
|
||||||
|
}
|
||||||
|
if w.sizerr() {
|
||||||
|
w.set_sizerr(true);
|
||||||
|
}
|
||||||
|
if w.pgaerr() {
|
||||||
|
w.set_pgaerr(true);
|
||||||
|
}
|
||||||
|
if w.wrperr() {
|
||||||
|
w.set_wrperr(true);
|
||||||
|
}
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
if w.progerr() {
|
||||||
|
w.set_progerr(true);
|
||||||
|
}
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
if w.operr() {
|
||||||
|
w.set_operr(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
|
||||||
|
loop {
|
||||||
|
let sr = pac::FLASH.sr().read();
|
||||||
|
|
||||||
|
if !sr.bsy() {
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
if sr.progerr() {
|
||||||
|
return Err(Error::Prog);
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.wrperr() {
|
||||||
|
return Err(Error::Protected);
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.pgaerr() {
|
||||||
|
return Err(Error::Unaligned);
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.sizerr() {
|
||||||
|
return Err(Error::Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
if sr.miserr() {
|
||||||
|
return Err(Error::Miss);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
||||||
|
if sr.pgserr() {
|
||||||
|
return Err(Error::Seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,5 @@
|
|||||||
use crate::pac;
|
|
||||||
use crate::peripherals::FLASH;
|
use crate::peripherals::FLASH;
|
||||||
use core::convert::TryInto;
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::ptr::write_volatile;
|
|
||||||
use embassy::util::Unborrow;
|
use embassy::util::Unborrow;
|
||||||
use embassy_hal_common::unborrow;
|
use embassy_hal_common::unborrow;
|
||||||
|
|
||||||
@ -17,6 +14,12 @@ pub use crate::pac::FLASH_SIZE;
|
|||||||
pub use crate::pac::WRITE_SIZE;
|
pub use crate::pac::WRITE_SIZE;
|
||||||
const FLASH_END: usize = FLASH_BASE + FLASH_SIZE;
|
const FLASH_END: usize = FLASH_BASE + FLASH_SIZE;
|
||||||
|
|
||||||
|
#[cfg_attr(any(flash_wl, flash_wb, flash_l0, flash_l1, flash_l4), path = "l.rs")]
|
||||||
|
#[cfg_attr(flash_f3, path = "f3.rs")]
|
||||||
|
#[cfg_attr(flash_f7, path = "f7.rs")]
|
||||||
|
#[cfg_attr(flash_h7, path = "h7.rs")]
|
||||||
|
mod family;
|
||||||
|
|
||||||
pub struct Flash<'d> {
|
pub struct Flash<'d> {
|
||||||
_inner: FLASH,
|
_inner: FLASH,
|
||||||
_phantom: PhantomData<&'d mut FLASH>,
|
_phantom: PhantomData<&'d mut FLASH>,
|
||||||
@ -33,37 +36,13 @@ impl<'d> Flash<'d> {
|
|||||||
|
|
||||||
pub fn unlock(p: impl Unborrow<Target = FLASH>) -> Self {
|
pub fn unlock(p: impl Unborrow<Target = FLASH>) -> Self {
|
||||||
let flash = Self::new(p);
|
let flash = Self::new(p);
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
unsafe {
|
|
||||||
pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
|
|
||||||
pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(flash_l0))]
|
unsafe { family::unlock() };
|
||||||
unsafe {
|
|
||||||
pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x89ABCDEF));
|
|
||||||
pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x02030405));
|
|
||||||
|
|
||||||
pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x8C9DAEBF));
|
|
||||||
pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x13141516));
|
|
||||||
}
|
|
||||||
flash
|
flash
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lock(&mut self) {
|
pub fn lock(&mut self) {
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
unsafe { family::lock() };
|
||||||
unsafe {
|
|
||||||
pac::FLASH.cr().modify(|w| w.set_lock(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(flash_l0))]
|
|
||||||
unsafe {
|
|
||||||
pac::FLASH.pecr().modify(|w| {
|
|
||||||
w.set_optlock(true);
|
|
||||||
w.set_prglock(true);
|
|
||||||
w.set_pelock(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
|
pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
|
||||||
@ -89,36 +68,7 @@ impl<'d> Flash<'d> {
|
|||||||
|
|
||||||
self.clear_all_err();
|
self.clear_all_err();
|
||||||
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
unsafe { family::blocking_write(offset, buf) }
|
||||||
unsafe {
|
|
||||||
pac::FLASH.cr().write(|w| w.set_pg(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ret: Result<(), Error> = Ok(());
|
|
||||||
let mut offset = offset;
|
|
||||||
for chunk in buf.chunks(WRITE_SIZE) {
|
|
||||||
for val in chunk.chunks(4) {
|
|
||||||
unsafe {
|
|
||||||
write_volatile(
|
|
||||||
offset as *mut u32,
|
|
||||||
u32::from_le_bytes(val[0..4].try_into().unwrap()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
offset += val.len() as u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = self.blocking_wait_ready();
|
|
||||||
if ret.is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
unsafe {
|
|
||||||
pac::FLASH.cr().write(|w| w.set_pg(false))
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
|
pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
|
||||||
@ -127,135 +77,17 @@ impl<'d> Flash<'d> {
|
|||||||
if to < from || to as usize > FLASH_END {
|
if to < from || to as usize > FLASH_END {
|
||||||
return Err(Error::Size);
|
return Err(Error::Size);
|
||||||
}
|
}
|
||||||
if from as usize % ERASE_SIZE != 0 || to as usize % ERASE_SIZE != 0 {
|
if ((to - from) as usize % ERASE_SIZE) != 0 {
|
||||||
return Err(Error::Unaligned);
|
return Err(Error::Unaligned);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.clear_all_err();
|
self.clear_all_err();
|
||||||
|
|
||||||
for page in (from..to).step_by(ERASE_SIZE) {
|
unsafe { family::blocking_erase(from, to) }
|
||||||
#[cfg(any(flash_l0, flash_l1))]
|
|
||||||
unsafe {
|
|
||||||
pac::FLASH.pecr().modify(|w| {
|
|
||||||
w.set_erase(true);
|
|
||||||
w.set_prog(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
write_volatile(page as *mut u32, 0xFFFFFFFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
unsafe {
|
|
||||||
let idx = page / ERASE_SIZE as u32;
|
|
||||||
|
|
||||||
pac::FLASH.cr().modify(|w| {
|
|
||||||
w.set_per(true);
|
|
||||||
w.set_pnb(idx as u8);
|
|
||||||
#[cfg(any(flash_wl, flash_wb))]
|
|
||||||
w.set_strt(true);
|
|
||||||
#[cfg(any(flash_l4))]
|
|
||||||
w.set_start(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret: Result<(), Error> = self.blocking_wait_ready();
|
|
||||||
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
unsafe {
|
|
||||||
pac::FLASH.cr().modify(|w| w.set_per(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(flash_l0, flash_l1))]
|
|
||||||
unsafe {
|
|
||||||
pac::FLASH.pecr().modify(|w| {
|
|
||||||
w.set_erase(false);
|
|
||||||
w.set_prog(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.clear_all_err();
|
|
||||||
if ret.is_err() {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn blocking_wait_ready(&self) -> Result<(), Error> {
|
|
||||||
loop {
|
|
||||||
let sr = unsafe { pac::FLASH.sr().read() };
|
|
||||||
|
|
||||||
if !sr.bsy() {
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
if sr.progerr() {
|
|
||||||
return Err(Error::Prog);
|
|
||||||
}
|
|
||||||
|
|
||||||
if sr.wrperr() {
|
|
||||||
return Err(Error::Protected);
|
|
||||||
}
|
|
||||||
|
|
||||||
if sr.pgaerr() {
|
|
||||||
return Err(Error::Unaligned);
|
|
||||||
}
|
|
||||||
|
|
||||||
if sr.sizerr() {
|
|
||||||
return Err(Error::Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
if sr.miserr() {
|
|
||||||
return Err(Error::Miss);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
if sr.pgserr() {
|
|
||||||
return Err(Error::Seq);
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_all_err(&mut self) {
|
fn clear_all_err(&mut self) {
|
||||||
unsafe {
|
unsafe { family::clear_all_err() };
|
||||||
pac::FLASH.sr().modify(|w| {
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4, flash_l0))]
|
|
||||||
if w.rderr() {
|
|
||||||
w.set_rderr(false);
|
|
||||||
}
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
if w.fasterr() {
|
|
||||||
w.set_fasterr(false);
|
|
||||||
}
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
if w.miserr() {
|
|
||||||
w.set_miserr(false);
|
|
||||||
}
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
if w.pgserr() {
|
|
||||||
w.set_pgserr(false);
|
|
||||||
}
|
|
||||||
if w.sizerr() {
|
|
||||||
w.set_sizerr(false);
|
|
||||||
}
|
|
||||||
if w.pgaerr() {
|
|
||||||
w.set_pgaerr(false);
|
|
||||||
}
|
|
||||||
if w.wrperr() {
|
|
||||||
w.set_wrperr(false);
|
|
||||||
}
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
if w.progerr() {
|
|
||||||
w.set_progerr(false);
|
|
||||||
}
|
|
||||||
#[cfg(any(flash_wl, flash_wb, flash_l4))]
|
|
||||||
if w.operr() {
|
|
||||||
w.set_operr(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +106,7 @@ pub enum Error {
|
|||||||
Seq,
|
Seq,
|
||||||
Protected,
|
Protected,
|
||||||
Unaligned,
|
Unaligned,
|
||||||
|
Parallelism,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> ErrorType for Flash<'d> {
|
impl<'d> ErrorType for Flash<'d> {
|
||||||
|
@ -39,7 +39,7 @@ pub mod can;
|
|||||||
pub mod dac;
|
pub mod dac;
|
||||||
#[cfg(dcmi)]
|
#[cfg(dcmi)]
|
||||||
pub mod dcmi;
|
pub mod dcmi;
|
||||||
#[cfg(all(eth, feature = "net"))]
|
#[cfg(eth)]
|
||||||
pub mod eth;
|
pub mod eth;
|
||||||
#[cfg(feature = "exti")]
|
#[cfg(feature = "exti")]
|
||||||
pub mod exti;
|
pub mod exti;
|
||||||
@ -50,7 +50,9 @@ pub mod i2c;
|
|||||||
|
|
||||||
#[cfg(crc)]
|
#[cfg(crc)]
|
||||||
pub mod crc;
|
pub mod crc;
|
||||||
#[cfg(any(flash_l0, flash_l1, flash_wl, flash_wb, flash_l4))]
|
#[cfg(any(
|
||||||
|
flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f7, flash_h7
|
||||||
|
))]
|
||||||
pub mod flash;
|
pub mod flash;
|
||||||
pub mod pwm;
|
pub mod pwm;
|
||||||
#[cfg(rng)]
|
#[cfg(rng)]
|
||||||
@ -61,7 +63,7 @@ pub mod sdmmc;
|
|||||||
pub mod spi;
|
pub mod spi;
|
||||||
#[cfg(usart)]
|
#[cfg(usart)]
|
||||||
pub mod usart;
|
pub mod usart;
|
||||||
#[cfg(feature = "usb-otg")]
|
#[cfg(any(otgfs, otghs))]
|
||||||
pub mod usb_otg;
|
pub mod usb_otg;
|
||||||
|
|
||||||
#[cfg(feature = "subghz")]
|
#[cfg(feature = "subghz")]
|
||||||
|
238
embassy-stm32/src/usart/buffered.rs
Normal file
238
embassy-stm32/src/usart/buffered.rs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
use atomic_polyfill::{compiler_fence, Ordering};
|
||||||
|
use core::future::Future;
|
||||||
|
use core::task::Poll;
|
||||||
|
use embassy::waitqueue::WakerRegistration;
|
||||||
|
use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
||||||
|
use embassy_hal_common::ring_buffer::RingBuffer;
|
||||||
|
use futures::future::poll_fn;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct State<'d, T: Instance>(StateStorage<StateInner<'d, T>>);
|
||||||
|
impl<'d, T: Instance> State<'d, T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(StateStorage::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StateInner<'d, T: Instance> {
|
||||||
|
phantom: PhantomData<&'d mut T>,
|
||||||
|
|
||||||
|
rx_waker: WakerRegistration,
|
||||||
|
rx: RingBuffer<'d>,
|
||||||
|
|
||||||
|
tx_waker: WakerRegistration,
|
||||||
|
tx: RingBuffer<'d>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {}
|
||||||
|
unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {}
|
||||||
|
|
||||||
|
pub struct BufferedUart<'d, T: Instance> {
|
||||||
|
inner: PeripheralMutex<'d, StateInner<'d, T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> BufferedUart<'d, T> {
|
||||||
|
pub unsafe fn new(
|
||||||
|
state: &'d mut State<'d, T>,
|
||||||
|
_uart: Uart<'d, T, NoDma, NoDma>,
|
||||||
|
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
||||||
|
tx_buffer: &'d mut [u8],
|
||||||
|
rx_buffer: &'d mut [u8],
|
||||||
|
) -> BufferedUart<'d, T> {
|
||||||
|
unborrow!(irq);
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
r.cr1().modify(|w| {
|
||||||
|
w.set_rxneie(true);
|
||||||
|
w.set_idleie(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
inner: PeripheralMutex::new_unchecked(irq, &mut state.0, move || StateInner {
|
||||||
|
phantom: PhantomData,
|
||||||
|
tx: RingBuffer::new(tx_buffer),
|
||||||
|
tx_waker: WakerRegistration::new(),
|
||||||
|
|
||||||
|
rx: RingBuffer::new(rx_buffer),
|
||||||
|
rx_waker: WakerRegistration::new(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> StateInner<'d, T>
|
||||||
|
where
|
||||||
|
Self: 'd,
|
||||||
|
{
|
||||||
|
fn on_rx(&mut self) {
|
||||||
|
let r = T::regs();
|
||||||
|
unsafe {
|
||||||
|
let sr = sr(r).read();
|
||||||
|
clear_interrupt_flags(r, sr);
|
||||||
|
|
||||||
|
// This read also clears the error and idle interrupt flags on v1.
|
||||||
|
let b = rdr(r).read_volatile();
|
||||||
|
|
||||||
|
if sr.rxne() {
|
||||||
|
if sr.pe() {
|
||||||
|
warn!("Parity error");
|
||||||
|
}
|
||||||
|
if sr.fe() {
|
||||||
|
warn!("Framing error");
|
||||||
|
}
|
||||||
|
if sr.ne() {
|
||||||
|
warn!("Noise error");
|
||||||
|
}
|
||||||
|
if sr.ore() {
|
||||||
|
warn!("Overrun error");
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = self.rx.push_buf();
|
||||||
|
if !buf.is_empty() {
|
||||||
|
buf[0] = b;
|
||||||
|
self.rx.push(1);
|
||||||
|
} else {
|
||||||
|
warn!("RX buffer full, discard received byte");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.rx.is_full() {
|
||||||
|
self.rx_waker.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.idle() {
|
||||||
|
self.rx_waker.wake();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_tx(&mut self) {
|
||||||
|
let r = T::regs();
|
||||||
|
unsafe {
|
||||||
|
if sr(r).read().txe() {
|
||||||
|
let buf = self.tx.pop_buf();
|
||||||
|
if !buf.is_empty() {
|
||||||
|
r.cr1().modify(|w| {
|
||||||
|
w.set_txeie(true);
|
||||||
|
});
|
||||||
|
tdr(r).write_volatile(buf[0].into());
|
||||||
|
self.tx.pop(1);
|
||||||
|
self.tx_waker.wake();
|
||||||
|
} else {
|
||||||
|
// Disable interrupt until we have something to transmit again
|
||||||
|
r.cr1().modify(|w| {
|
||||||
|
w.set_txeie(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> PeripheralState for StateInner<'d, T>
|
||||||
|
where
|
||||||
|
Self: 'd,
|
||||||
|
{
|
||||||
|
type Interrupt = T::Interrupt;
|
||||||
|
fn on_interrupt(&mut self) {
|
||||||
|
self.on_rx();
|
||||||
|
self.on_tx();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io::Error for Error {
|
||||||
|
fn kind(&self) -> embedded_io::ErrorKind {
|
||||||
|
embedded_io::ErrorKind::Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embedded_io::asynch::Read for BufferedUart<'d, T> {
|
||||||
|
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
let mut do_pend = false;
|
||||||
|
let res = self.inner.with(|state| {
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
// We have data ready in buffer? Return it.
|
||||||
|
let data = state.rx.pop_buf();
|
||||||
|
if !data.is_empty() {
|
||||||
|
let len = data.len().min(buf.len());
|
||||||
|
buf[..len].copy_from_slice(&data[..len]);
|
||||||
|
|
||||||
|
if state.rx.is_full() {
|
||||||
|
do_pend = true;
|
||||||
|
}
|
||||||
|
state.rx.pop(len);
|
||||||
|
|
||||||
|
return Poll::Ready(Ok(len));
|
||||||
|
}
|
||||||
|
|
||||||
|
state.rx_waker.register(cx.waker());
|
||||||
|
Poll::Pending
|
||||||
|
});
|
||||||
|
|
||||||
|
if do_pend {
|
||||||
|
self.inner.pend();
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> embedded_io::asynch::Write for BufferedUart<'d, T> {
|
||||||
|
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
let (poll, empty) = self.inner.with(|state| {
|
||||||
|
let empty = state.tx.is_empty();
|
||||||
|
let tx_buf = state.tx.push_buf();
|
||||||
|
if tx_buf.is_empty() {
|
||||||
|
state.tx_waker.register(cx.waker());
|
||||||
|
return (Poll::Pending, empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = core::cmp::min(tx_buf.len(), buf.len());
|
||||||
|
tx_buf[..n].copy_from_slice(&buf[..n]);
|
||||||
|
state.tx.push(n);
|
||||||
|
|
||||||
|
(Poll::Ready(Ok(n)), empty)
|
||||||
|
});
|
||||||
|
if empty {
|
||||||
|
self.inner.pend();
|
||||||
|
}
|
||||||
|
poll
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||||
|
poll_fn(move |cx| {
|
||||||
|
self.inner.with(|state| {
|
||||||
|
if !state.tx.is_empty() {
|
||||||
|
state.tx_waker.register(cx.waker());
|
||||||
|
return Poll::Pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -424,227 +424,10 @@ cfg_if::cfg_if! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
pub use buffered::*;
|
pub use buffered::*;
|
||||||
mod buffered {
|
#[cfg(feature = "nightly")]
|
||||||
use atomic_polyfill::{compiler_fence, Ordering};
|
mod buffered;
|
||||||
use core::pin::Pin;
|
|
||||||
use core::task::Context;
|
|
||||||
use core::task::Poll;
|
|
||||||
use embassy::waitqueue::WakerRegistration;
|
|
||||||
use embassy_hal_common::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
|
||||||
use embassy_hal_common::ring_buffer::RingBuffer;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub struct State<'d, T: Instance>(StateStorage<StateInner<'d, T>>);
|
|
||||||
impl<'d, T: Instance> State<'d, T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(StateStorage::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StateInner<'d, T: Instance> {
|
|
||||||
phantom: PhantomData<&'d mut T>,
|
|
||||||
|
|
||||||
rx_waker: WakerRegistration,
|
|
||||||
rx: RingBuffer<'d>,
|
|
||||||
|
|
||||||
tx_waker: WakerRegistration,
|
|
||||||
tx: RingBuffer<'d>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {}
|
|
||||||
unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {}
|
|
||||||
|
|
||||||
pub struct BufferedUart<'d, T: Instance> {
|
|
||||||
inner: PeripheralMutex<'d, StateInner<'d, T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> BufferedUart<'d, T> {
|
|
||||||
pub unsafe fn new(
|
|
||||||
state: &'d mut State<'d, T>,
|
|
||||||
_uart: Uart<'d, T, NoDma, NoDma>,
|
|
||||||
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
|
||||||
tx_buffer: &'d mut [u8],
|
|
||||||
rx_buffer: &'d mut [u8],
|
|
||||||
) -> BufferedUart<'d, T> {
|
|
||||||
unborrow!(irq);
|
|
||||||
|
|
||||||
let r = T::regs();
|
|
||||||
r.cr1().modify(|w| {
|
|
||||||
w.set_rxneie(true);
|
|
||||||
w.set_idleie(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
inner: PeripheralMutex::new_unchecked(irq, &mut state.0, move || StateInner {
|
|
||||||
phantom: PhantomData,
|
|
||||||
tx: RingBuffer::new(tx_buffer),
|
|
||||||
tx_waker: WakerRegistration::new(),
|
|
||||||
|
|
||||||
rx: RingBuffer::new(rx_buffer),
|
|
||||||
rx_waker: WakerRegistration::new(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> StateInner<'d, T>
|
|
||||||
where
|
|
||||||
Self: 'd,
|
|
||||||
{
|
|
||||||
fn on_rx(&mut self) {
|
|
||||||
let r = T::regs();
|
|
||||||
unsafe {
|
|
||||||
let sr = sr(r).read();
|
|
||||||
clear_interrupt_flags(r, sr);
|
|
||||||
|
|
||||||
// This read also clears the error and idle interrupt flags on v1.
|
|
||||||
let b = rdr(r).read_volatile();
|
|
||||||
|
|
||||||
if sr.rxne() {
|
|
||||||
if sr.pe() {
|
|
||||||
warn!("Parity error");
|
|
||||||
}
|
|
||||||
if sr.fe() {
|
|
||||||
warn!("Framing error");
|
|
||||||
}
|
|
||||||
if sr.ne() {
|
|
||||||
warn!("Noise error");
|
|
||||||
}
|
|
||||||
if sr.ore() {
|
|
||||||
warn!("Overrun error");
|
|
||||||
}
|
|
||||||
|
|
||||||
let buf = self.rx.push_buf();
|
|
||||||
if !buf.is_empty() {
|
|
||||||
buf[0] = b;
|
|
||||||
self.rx.push(1);
|
|
||||||
} else {
|
|
||||||
warn!("RX buffer full, discard received byte");
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.rx.is_full() {
|
|
||||||
self.rx_waker.wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if sr.idle() {
|
|
||||||
self.rx_waker.wake();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_tx(&mut self) {
|
|
||||||
let r = T::regs();
|
|
||||||
unsafe {
|
|
||||||
if sr(r).read().txe() {
|
|
||||||
let buf = self.tx.pop_buf();
|
|
||||||
if !buf.is_empty() {
|
|
||||||
r.cr1().modify(|w| {
|
|
||||||
w.set_txeie(true);
|
|
||||||
});
|
|
||||||
tdr(r).write_volatile(buf[0].into());
|
|
||||||
self.tx.pop(1);
|
|
||||||
self.tx_waker.wake();
|
|
||||||
} else {
|
|
||||||
// Disable interrupt until we have something to transmit again
|
|
||||||
r.cr1().modify(|w| {
|
|
||||||
w.set_txeie(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> PeripheralState for StateInner<'d, T>
|
|
||||||
where
|
|
||||||
Self: 'd,
|
|
||||||
{
|
|
||||||
type Interrupt = T::Interrupt;
|
|
||||||
fn on_interrupt(&mut self) {
|
|
||||||
self.on_rx();
|
|
||||||
self.on_tx();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embassy::io::AsyncBufRead for BufferedUart<'d, T> {
|
|
||||||
fn poll_fill_buf(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Result<&[u8], embassy::io::Error>> {
|
|
||||||
self.inner.with(|state| {
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
|
||||||
|
|
||||||
// We have data ready in buffer? Return it.
|
|
||||||
let buf = state.rx.pop_buf();
|
|
||||||
if !buf.is_empty() {
|
|
||||||
let buf: &[u8] = buf;
|
|
||||||
// Safety: buffer lives as long as uart
|
|
||||||
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
|
|
||||||
return Poll::Ready(Ok(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
state.rx_waker.register(cx.waker());
|
|
||||||
Poll::<Result<&[u8], embassy::io::Error>>::Pending
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn consume(mut self: Pin<&mut Self>, amt: usize) {
|
|
||||||
let signal = self.inner.with(|state| {
|
|
||||||
let full = state.rx.is_full();
|
|
||||||
state.rx.pop(amt);
|
|
||||||
full
|
|
||||||
});
|
|
||||||
if signal {
|
|
||||||
self.inner.pend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embassy::io::AsyncWrite for BufferedUart<'d, T> {
|
|
||||||
fn poll_write(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<Result<usize, embassy::io::Error>> {
|
|
||||||
let (poll, empty) = self.inner.with(|state| {
|
|
||||||
let empty = state.tx.is_empty();
|
|
||||||
let tx_buf = state.tx.push_buf();
|
|
||||||
if tx_buf.is_empty() {
|
|
||||||
state.tx_waker.register(cx.waker());
|
|
||||||
return (Poll::Pending, empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = core::cmp::min(tx_buf.len(), buf.len());
|
|
||||||
tx_buf[..n].copy_from_slice(&buf[..n]);
|
|
||||||
state.tx.push(n);
|
|
||||||
|
|
||||||
(Poll::Ready(Ok(n)), empty)
|
|
||||||
});
|
|
||||||
if empty {
|
|
||||||
self.inner.pend();
|
|
||||||
}
|
|
||||||
poll
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Result<(), embassy::io::Error>> {
|
|
||||||
self.inner.with(|state| {
|
|
||||||
if !state.tx.is_empty() {
|
|
||||||
state.tx_waker.register(cx.waker());
|
|
||||||
return Poll::Pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(usart_v1)]
|
#[cfg(usart_v1)]
|
||||||
fn tdr(r: crate::pac::usart::Usart) -> *mut u8 {
|
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(usart_v1)]
|
#[cfg(usart_v1)]
|
||||||
|
#[allow(unused)]
|
||||||
unsafe fn clear_interrupt_flags(_r: crate::pac::usart::Usart, _sr: regs::Sr) {
|
unsafe fn clear_interrupt_flags(_r: crate::pac::usart::Usart, _sr: regs::Sr) {
|
||||||
// On v1 the flags are cleared implicitly by reads and writes to DR.
|
// On v1 the flags are cleared implicitly by reads and writes to DR.
|
||||||
}
|
}
|
||||||
@ -682,6 +466,7 @@ fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Ixr, crate::
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(usart_v2)]
|
#[cfg(usart_v2)]
|
||||||
|
#[allow(unused)]
|
||||||
unsafe fn clear_interrupt_flags(r: crate::pac::usart::Usart, sr: regs::Ixr) {
|
unsafe fn clear_interrupt_flags(r: crate::pac::usart::Usart, sr: regs::Ixr) {
|
||||||
r.icr().write(|w| *w = sr);
|
r.icr().write(|w| *w = sr);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use embassy::util::Unborrow;
|
use embassy::util::Unborrow;
|
||||||
use embassy_hal_common::unborrow;
|
use embassy_hal_common::unborrow;
|
||||||
use synopsys_usb_otg::{PhyType, UsbPeripheral};
|
|
||||||
|
|
||||||
use crate::gpio::sealed::AFType;
|
use crate::gpio::sealed::AFType;
|
||||||
use crate::gpio::Speed;
|
use crate::gpio::Speed;
|
||||||
use crate::{peripherals, rcc::RccPeripheral};
|
use crate::{peripherals, rcc::RccPeripheral};
|
||||||
|
|
||||||
pub use embassy_hal_common::usb::*;
|
|
||||||
pub use synopsys_usb_otg::UsbBus;
|
|
||||||
|
|
||||||
macro_rules! config_ulpi_pins {
|
macro_rules! config_ulpi_pins {
|
||||||
($($pin:ident),*) => {
|
($($pin:ident),*) => {
|
||||||
unborrow!($($pin),*);
|
unborrow!($($pin),*);
|
||||||
@ -23,9 +19,24 @@ macro_rules! config_ulpi_pins {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB PHY type
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum PhyType {
|
||||||
|
/// Internal Full-Speed PHY
|
||||||
|
///
|
||||||
|
/// Available on most High-Speed peripherals.
|
||||||
|
InternalFullSpeed,
|
||||||
|
/// Internal High-Speed PHY
|
||||||
|
///
|
||||||
|
/// Available on a few STM32 chips.
|
||||||
|
InternalHighSpeed,
|
||||||
|
/// External ULPI High-Speed PHY
|
||||||
|
ExternalHighSpeed,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct UsbOtg<'d, T: Instance> {
|
pub struct UsbOtg<'d, T: Instance> {
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
phy_type: PhyType,
|
_phy_type: PhyType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> UsbOtg<'d, T> {
|
impl<'d, T: Instance> UsbOtg<'d, T> {
|
||||||
@ -44,7 +55,7 @@ impl<'d, T: Instance> UsbOtg<'d, T> {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
phy_type: PhyType::InternalFullSpeed,
|
_phy_type: PhyType::InternalFullSpeed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +82,7 @@ impl<'d, T: Instance> UsbOtg<'d, T> {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
phy_type: PhyType::ExternalHighSpeed,
|
_phy_type: PhyType::ExternalHighSpeed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,29 +94,6 @@ impl<'d, T: Instance> Drop for UsbOtg<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'d, T: Instance> Send for UsbOtg<'d, T> {}
|
|
||||||
unsafe impl<'d, T: Instance> Sync for UsbOtg<'d, T> {}
|
|
||||||
|
|
||||||
unsafe impl<'d, T: Instance> UsbPeripheral for UsbOtg<'d, T> {
|
|
||||||
const REGISTERS: *const () = T::REGISTERS;
|
|
||||||
const HIGH_SPEED: bool = T::HIGH_SPEED;
|
|
||||||
const FIFO_DEPTH_WORDS: usize = T::FIFO_DEPTH_WORDS;
|
|
||||||
const ENDPOINT_COUNT: usize = T::ENDPOINT_COUNT;
|
|
||||||
|
|
||||||
fn enable() {
|
|
||||||
<T as crate::rcc::sealed::RccPeripheral>::enable();
|
|
||||||
<T as crate::rcc::sealed::RccPeripheral>::reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn phy_type(&self) -> PhyType {
|
|
||||||
self.phy_type
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ahb_frequency_hz(&self) -> u32 {
|
|
||||||
<T as crate::rcc::sealed::RccPeripheral>::frequency().0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
pub trait Instance {
|
pub trait Instance {
|
||||||
const REGISTERS: *const ();
|
const REGISTERS: *const ();
|
||||||
@ -177,7 +165,7 @@ foreach_peripheral!(
|
|||||||
const FIFO_DEPTH_WORDS: usize = 512;
|
const FIFO_DEPTH_WORDS: usize = 512;
|
||||||
const ENDPOINT_COUNT: usize = 8;
|
const ENDPOINT_COUNT: usize = 8;
|
||||||
} else {
|
} else {
|
||||||
compile_error!("USB_OTG_FS peripheral is not supported by this chip. Disable \"usb-otg-fs\" feature or select a different chip.");
|
compile_error!("USB_OTG_FS peripheral is not supported by this chip.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,7 +202,7 @@ foreach_peripheral!(
|
|||||||
const FIFO_DEPTH_WORDS: usize = 1024;
|
const FIFO_DEPTH_WORDS: usize = 1024;
|
||||||
const ENDPOINT_COUNT: usize = 9;
|
const ENDPOINT_COUNT: usize = 9;
|
||||||
} else {
|
} else {
|
||||||
compile_error!("USB_OTG_HS peripheral is not supported by this chip. Disable \"usb-otg-hs\" feature or select a different chip.");
|
compile_error!("USB_OTG_HS peripheral is not supported by this chip.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,12 +210,3 @@ foreach_peripheral!(
|
|||||||
impl Instance for peripherals::$inst {}
|
impl Instance for peripherals::$inst {}
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach_interrupt!(
|
|
||||||
($inst:ident, otgfs, $block:ident, GLOBAL, $irq:ident) => {
|
|
||||||
unsafe impl USBInterrupt for crate::interrupt::$irq {}
|
|
||||||
};
|
|
||||||
($inst:ident, otghs, $block:ident, GLOBAL, $irq:ident) => {
|
|
||||||
unsafe impl USBInterrupt for crate::interrupt::$irq {}
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
@ -1,142 +0,0 @@
|
|||||||
/// Categories of errors that can occur.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum Error {
|
|
||||||
/// An entity was not found, often a file.
|
|
||||||
NotFound,
|
|
||||||
/// The operation lacked the necessary privileges to complete.
|
|
||||||
PermissionDenied,
|
|
||||||
/// The connection was refused by the remote server.
|
|
||||||
ConnectionRefused,
|
|
||||||
/// The connection was reset by the remote server.
|
|
||||||
ConnectionReset,
|
|
||||||
/// The connection was aborted (terminated) by the remote server.
|
|
||||||
ConnectionAborted,
|
|
||||||
/// The network operation failed because it was not connected yet.
|
|
||||||
NotConnected,
|
|
||||||
/// A socket address could not be bound because the address is already in
|
|
||||||
/// use elsewhere.
|
|
||||||
AddrInUse,
|
|
||||||
/// A nonexistent interface was requested or the requested address was not
|
|
||||||
/// local.
|
|
||||||
AddrNotAvailable,
|
|
||||||
/// The operation failed because a pipe was closed.
|
|
||||||
BrokenPipe,
|
|
||||||
/// An entity already exists, often a file.
|
|
||||||
AlreadyExists,
|
|
||||||
/// The operation needs to block to complete, but the blocking operation was
|
|
||||||
/// requested to not occur.
|
|
||||||
WouldBlock,
|
|
||||||
/// A parameter was incorrect.
|
|
||||||
InvalidInput,
|
|
||||||
/// Data not valid for the operation were encountered.
|
|
||||||
///
|
|
||||||
/// Unlike [`InvalidInput`], this typically means that the operation
|
|
||||||
/// parameters were valid, however the error was caused by malformed
|
|
||||||
/// input data.
|
|
||||||
///
|
|
||||||
/// For example, a function that reads a file into a string will error with
|
|
||||||
/// `InvalidData` if the file's contents are not valid UTF-8.
|
|
||||||
///
|
|
||||||
/// [`InvalidInput`]: #variant.InvalidInput
|
|
||||||
InvalidData,
|
|
||||||
/// The I/O operation's timeout expired, causing it to be canceled.
|
|
||||||
TimedOut,
|
|
||||||
/// An error returned when an operation could not be completed because a
|
|
||||||
/// call to [`write`] returned [`Ok(0)`].
|
|
||||||
///
|
|
||||||
/// This typically means that an operation could only succeed if it wrote a
|
|
||||||
/// particular number of bytes but only a smaller number of bytes could be
|
|
||||||
/// written.
|
|
||||||
///
|
|
||||||
/// [`write`]: ../../std/io/trait.Write.html#tymethod.write
|
|
||||||
/// [`Ok(0)`]: ../../std/io/type.Result.html
|
|
||||||
WriteZero,
|
|
||||||
/// This operation was interrupted.
|
|
||||||
///
|
|
||||||
/// Interrupted operations can typically be retried.
|
|
||||||
Interrupted,
|
|
||||||
|
|
||||||
/// An error returned when an operation could not be completed because an
|
|
||||||
/// "end of file" was reached prematurely.
|
|
||||||
///
|
|
||||||
/// This typically means that an operation could only succeed if it read a
|
|
||||||
/// particular number of bytes but only a smaller number of bytes could be
|
|
||||||
/// read.
|
|
||||||
UnexpectedEof,
|
|
||||||
|
|
||||||
/// An operation would have read more data if the given buffer was large.
|
|
||||||
///
|
|
||||||
/// This typically means that the buffer has been filled with the first N bytes
|
|
||||||
/// of the read data.
|
|
||||||
Truncated,
|
|
||||||
|
|
||||||
/// Any I/O error not part of this list.
|
|
||||||
Other,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl From<std::io::Error> for Error {
|
|
||||||
fn from(err: std::io::Error) -> Error {
|
|
||||||
match err.kind() {
|
|
||||||
std::io::ErrorKind::NotFound => Error::NotFound,
|
|
||||||
std::io::ErrorKind::PermissionDenied => Error::PermissionDenied,
|
|
||||||
std::io::ErrorKind::ConnectionRefused => Error::ConnectionRefused,
|
|
||||||
std::io::ErrorKind::ConnectionReset => Error::ConnectionReset,
|
|
||||||
std::io::ErrorKind::ConnectionAborted => Error::ConnectionAborted,
|
|
||||||
std::io::ErrorKind::NotConnected => Error::NotConnected,
|
|
||||||
std::io::ErrorKind::AddrInUse => Error::AddrInUse,
|
|
||||||
std::io::ErrorKind::AddrNotAvailable => Error::AddrNotAvailable,
|
|
||||||
std::io::ErrorKind::BrokenPipe => Error::BrokenPipe,
|
|
||||||
std::io::ErrorKind::AlreadyExists => Error::AlreadyExists,
|
|
||||||
std::io::ErrorKind::WouldBlock => Error::WouldBlock,
|
|
||||||
std::io::ErrorKind::InvalidInput => Error::InvalidInput,
|
|
||||||
std::io::ErrorKind::InvalidData => Error::InvalidData,
|
|
||||||
std::io::ErrorKind::TimedOut => Error::TimedOut,
|
|
||||||
std::io::ErrorKind::WriteZero => Error::WriteZero,
|
|
||||||
std::io::ErrorKind::Interrupted => Error::Interrupted,
|
|
||||||
std::io::ErrorKind::UnexpectedEof => Error::UnexpectedEof,
|
|
||||||
_ => Error::Other,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl From<Error> for std::io::Error {
|
|
||||||
fn from(e: Error) -> Self {
|
|
||||||
let kind = match e {
|
|
||||||
Error::NotFound => std::io::ErrorKind::NotFound,
|
|
||||||
Error::PermissionDenied => std::io::ErrorKind::PermissionDenied,
|
|
||||||
Error::ConnectionRefused => std::io::ErrorKind::ConnectionRefused,
|
|
||||||
Error::ConnectionReset => std::io::ErrorKind::ConnectionReset,
|
|
||||||
Error::ConnectionAborted => std::io::ErrorKind::ConnectionAborted,
|
|
||||||
Error::NotConnected => std::io::ErrorKind::NotConnected,
|
|
||||||
Error::AddrInUse => std::io::ErrorKind::AddrInUse,
|
|
||||||
Error::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable,
|
|
||||||
Error::BrokenPipe => std::io::ErrorKind::BrokenPipe,
|
|
||||||
Error::AlreadyExists => std::io::ErrorKind::AlreadyExists,
|
|
||||||
Error::WouldBlock => std::io::ErrorKind::WouldBlock,
|
|
||||||
Error::InvalidInput => std::io::ErrorKind::InvalidInput,
|
|
||||||
Error::InvalidData => std::io::ErrorKind::InvalidData,
|
|
||||||
Error::TimedOut => std::io::ErrorKind::TimedOut,
|
|
||||||
Error::WriteZero => std::io::ErrorKind::WriteZero,
|
|
||||||
Error::Interrupted => std::io::ErrorKind::Interrupted,
|
|
||||||
Error::UnexpectedEof => std::io::ErrorKind::UnexpectedEof,
|
|
||||||
Error::Truncated => std::io::ErrorKind::Other,
|
|
||||||
Error::Other => std::io::ErrorKind::Other,
|
|
||||||
};
|
|
||||||
std::io::Error::new(kind, "embassy::io::Error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
write!(f, "{:?}", self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl std::error::Error for Error {}
|
|
@ -1,11 +0,0 @@
|
|||||||
mod error;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
mod std;
|
|
||||||
mod traits;
|
|
||||||
mod util;
|
|
||||||
|
|
||||||
pub use self::error::*;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use self::std::*;
|
|
||||||
pub use self::traits::*;
|
|
||||||
pub use self::util::*;
|
|
@ -1,41 +0,0 @@
|
|||||||
use core::pin::Pin;
|
|
||||||
use core::task::{Context, Poll};
|
|
||||||
use futures::io as std_io;
|
|
||||||
|
|
||||||
use super::{AsyncBufRead, AsyncWrite, Result};
|
|
||||||
|
|
||||||
pub struct FromStdIo<T>(T);
|
|
||||||
|
|
||||||
impl<T> FromStdIo<T> {
|
|
||||||
pub fn new(inner: T) -> Self {
|
|
||||||
Self(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> {
|
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
|
|
||||||
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
|
||||||
unsafe { Pin::new_unchecked(inner) }
|
|
||||||
.poll_fill_buf(cx)
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
}
|
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
||||||
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
|
||||||
unsafe { Pin::new_unchecked(inner) }.consume(amt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> {
|
|
||||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
|
|
||||||
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
|
||||||
unsafe { Pin::new_unchecked(inner) }
|
|
||||||
.poll_write(cx, buf)
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
}
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
||||||
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
|
||||||
unsafe { Pin::new_unchecked(inner) }
|
|
||||||
.poll_flush(cx)
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,175 +0,0 @@
|
|||||||
use core::ops::DerefMut;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use core::task::{Context, Poll};
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
|
|
||||||
use super::error::Result;
|
|
||||||
|
|
||||||
/// Read bytes asynchronously.
|
|
||||||
///
|
|
||||||
/// This trait is analogous to the `std::io::BufRead` trait, but integrates
|
|
||||||
/// with the asynchronous task system. In particular, the `poll_fill_buf`
|
|
||||||
/// method, unlike `BufRead::fill_buf`, will automatically queue the current task
|
|
||||||
/// for wakeup and return if data is not yet available, rather than blocking
|
|
||||||
/// the calling thread.
|
|
||||||
pub trait AsyncBufRead {
|
|
||||||
/// Attempt to return the contents of the internal buffer, filling it with more data
|
|
||||||
/// from the inner reader if it is empty.
|
|
||||||
///
|
|
||||||
/// On success, returns `Poll::Ready(Ok(buf))`.
|
|
||||||
///
|
|
||||||
/// If no data is available for reading, the method returns
|
|
||||||
/// `Poll::Pending` and arranges for the current task (via
|
|
||||||
/// `cx.waker().wake_by_ref()`) to receive a notification when the object becomes
|
|
||||||
/// readable or is closed.
|
|
||||||
///
|
|
||||||
/// This function is a lower-level call. It needs to be paired with the
|
|
||||||
/// [`consume`] method to function properly. When calling this
|
|
||||||
/// method, none of the contents will be "read" in the sense that later
|
|
||||||
/// calling [`poll_fill_buf`] may return the same contents. As such, [`consume`] must
|
|
||||||
/// be called with the number of bytes that are consumed from this buffer to
|
|
||||||
/// ensure that the bytes are never returned twice.
|
|
||||||
///
|
|
||||||
/// [`poll_fill_buf`]: AsyncBufRead::poll_fill_buf
|
|
||||||
/// [`consume`]: AsyncBufRead::consume
|
|
||||||
///
|
|
||||||
/// An empty buffer returned indicates that the stream has reached EOF.
|
|
||||||
///
|
|
||||||
/// # Implementation
|
|
||||||
///
|
|
||||||
/// This function may not return errors of kind `WouldBlock` or
|
|
||||||
/// `Interrupted`. Implementations must convert `WouldBlock` into
|
|
||||||
/// `Poll::Pending` and either internally retry or convert
|
|
||||||
/// `Interrupted` into another error kind.
|
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>>;
|
|
||||||
|
|
||||||
/// Tells this buffer that `amt` bytes have been consumed from the buffer,
|
|
||||||
/// so they should no longer be returned in calls to [`poll_fill_buf`].
|
|
||||||
///
|
|
||||||
/// This function is a lower-level call. It needs to be paired with the
|
|
||||||
/// [`poll_fill_buf`] method to function properly. This function does
|
|
||||||
/// not perform any I/O, it simply informs this object that some amount of
|
|
||||||
/// its buffer, returned from [`poll_fill_buf`], has been consumed and should
|
|
||||||
/// no longer be returned. As such, this function may do odd things if
|
|
||||||
/// [`poll_fill_buf`] isn't called before calling it.
|
|
||||||
///
|
|
||||||
/// The `amt` must be `<=` the number of bytes in the buffer returned by
|
|
||||||
/// [`poll_fill_buf`].
|
|
||||||
///
|
|
||||||
/// [`poll_fill_buf`]: AsyncBufRead::poll_fill_buf
|
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write bytes asynchronously.
|
|
||||||
///
|
|
||||||
/// This trait is analogous to the `core::io::Write` trait, but integrates
|
|
||||||
/// with the asynchronous task system. In particular, the `poll_write`
|
|
||||||
/// method, unlike `Write::write`, will automatically queue the current task
|
|
||||||
/// for wakeup and return if the writer cannot take more data, rather than blocking
|
|
||||||
/// the calling thread.
|
|
||||||
pub trait AsyncWrite {
|
|
||||||
/// Attempt to write bytes from `buf` into the object.
|
|
||||||
///
|
|
||||||
/// On success, returns `Poll::Ready(Ok(num_bytes_written))`.
|
|
||||||
///
|
|
||||||
/// If the object is not ready for writing, the method returns
|
|
||||||
/// `Poll::Pending` and arranges for the current task (via
|
|
||||||
/// `cx.waker().wake_by_ref()`) to receive a notification when the object becomes
|
|
||||||
/// writable or is closed.
|
|
||||||
///
|
|
||||||
/// # Implementation
|
|
||||||
///
|
|
||||||
/// This function may not return errors of kind `WouldBlock` or
|
|
||||||
/// `Interrupted`. Implementations must convert `WouldBlock` into
|
|
||||||
/// `Poll::Pending` and either internally retry or convert
|
|
||||||
/// `Interrupted` into another error kind.
|
|
||||||
///
|
|
||||||
/// `poll_write` must try to make progress by flushing the underlying object if
|
|
||||||
/// that is the only way the underlying object can become writable again.
|
|
||||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>;
|
|
||||||
|
|
||||||
/// Attempt to flush the object, ensuring that any buffered data reach their destination.
|
|
||||||
///
|
|
||||||
/// On success, returns Poll::Ready(Ok(())).
|
|
||||||
///
|
|
||||||
/// If flushing cannot immediately complete, this method returns [Poll::Pending] and arranges for the
|
|
||||||
/// current task (via cx.waker()) to receive a notification when the object can make progress
|
|
||||||
/// towards flushing.
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! defer_async_read {
|
|
||||||
() => {
|
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
|
|
||||||
Pin::new(&mut **self.get_mut()).poll_fill_buf(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(mut self: Pin<&mut Self>, amt: usize) {
|
|
||||||
Pin::new(&mut **self).consume(amt)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for Box<T> {
|
|
||||||
defer_async_read!();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for &mut T {
|
|
||||||
defer_async_read!();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P> AsyncBufRead for Pin<P>
|
|
||||||
where
|
|
||||||
P: DerefMut + Unpin,
|
|
||||||
P::Target: AsyncBufRead,
|
|
||||||
{
|
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
|
|
||||||
self.get_mut().as_mut().poll_fill_buf(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
||||||
self.get_mut().as_mut().consume(amt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! deref_async_write {
|
|
||||||
() => {
|
|
||||||
fn poll_write(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<Result<usize>> {
|
|
||||||
Pin::new(&mut **self).poll_write(cx, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
||||||
Pin::new(&mut **self).poll_flush(cx)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> {
|
|
||||||
deref_async_write!();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for &mut T {
|
|
||||||
deref_async_write!();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P> AsyncWrite for Pin<P>
|
|
||||||
where
|
|
||||||
P: DerefMut + Unpin,
|
|
||||||
P::Target: AsyncWrite,
|
|
||||||
{
|
|
||||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
|
|
||||||
self.get_mut().as_mut().poll_write(cx, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
||||||
self.get_mut().as_mut().poll_flush(cx)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
use core::future::Future;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use core::task::{Context, Poll};
|
|
||||||
use futures::ready;
|
|
||||||
use pin_project::pin_project;
|
|
||||||
|
|
||||||
use crate::io::{AsyncBufRead, AsyncWrite, Error, Result};
|
|
||||||
|
|
||||||
/// Creates a future which copies all the bytes from one object to another.
|
|
||||||
///
|
|
||||||
/// The returned future will copy all the bytes read from this `AsyncBufRead` into the
|
|
||||||
/// `writer` specified. This future will only complete once the `reader` has hit
|
|
||||||
/// EOF and all bytes have been written to and flushed from the `writer`
|
|
||||||
/// provided.
|
|
||||||
///
|
|
||||||
/// On success the number of bytes is returned.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ``` ignore
|
|
||||||
/// # futures::executor::block_on(async {
|
|
||||||
/// use futures::io::{self, AsyncWriteExt, Cursor};
|
|
||||||
///
|
|
||||||
/// let reader = Cursor::new([1, 2, 3, 4]);
|
|
||||||
/// let mut writer = Cursor::new(vec![0u8; 5]);
|
|
||||||
///
|
|
||||||
/// let bytes = io::copy_buf(reader, &mut writer).await?;
|
|
||||||
/// writer.close().await?;
|
|
||||||
///
|
|
||||||
/// assert_eq!(bytes, 4);
|
|
||||||
/// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]);
|
|
||||||
/// # Ok::<(), Box<dyn std::error::Error>>(()) }).unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn copy_buf<R, W>(reader: R, writer: &mut W) -> CopyBuf<'_, R, W>
|
|
||||||
where
|
|
||||||
R: AsyncBufRead,
|
|
||||||
W: AsyncWrite + Unpin + ?Sized,
|
|
||||||
{
|
|
||||||
CopyBuf {
|
|
||||||
reader,
|
|
||||||
writer,
|
|
||||||
amt: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Future for the [`copy_buf()`] function.
|
|
||||||
#[pin_project]
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
||||||
pub struct CopyBuf<'a, R, W: ?Sized> {
|
|
||||||
#[pin]
|
|
||||||
reader: R,
|
|
||||||
writer: &'a mut W,
|
|
||||||
amt: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R, W> Future for CopyBuf<'_, R, W>
|
|
||||||
where
|
|
||||||
R: AsyncBufRead,
|
|
||||||
W: AsyncWrite + Unpin + ?Sized,
|
|
||||||
{
|
|
||||||
type Output = Result<usize>;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let mut this = self.project();
|
|
||||||
loop {
|
|
||||||
let buffer = ready!(this.reader.as_mut().poll_fill_buf(cx))?;
|
|
||||||
if buffer.is_empty() {
|
|
||||||
return Poll::Ready(Ok(*this.amt));
|
|
||||||
}
|
|
||||||
|
|
||||||
let i = ready!(Pin::new(&mut this.writer).poll_write(cx, buffer))?;
|
|
||||||
if i == 0 {
|
|
||||||
return Poll::Ready(Err(Error::WriteZero));
|
|
||||||
}
|
|
||||||
*this.amt += i;
|
|
||||||
this.reader.as_mut().consume(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
use super::super::error::Result;
|
|
||||||
use super::super::traits::AsyncBufRead;
|
|
||||||
|
|
||||||
pub struct Drain<'a, R: ?Sized> {
|
|
||||||
reader: &'a mut R,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: ?Sized + Unpin> Unpin for Drain<'_, R> {}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Drain<'a, R> {
|
|
||||||
pub(super) fn new(reader: &'a mut R) -> Self {
|
|
||||||
Self { reader }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for Drain<'a, R> {
|
|
||||||
type Output = Result<usize>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let Self { reader } = &mut *self;
|
|
||||||
let mut reader = Pin::new(reader);
|
|
||||||
|
|
||||||
let mut n = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match reader.as_mut().poll_fill_buf(cx) {
|
|
||||||
Poll::Pending => return Poll::Ready(Ok(n)),
|
|
||||||
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
|
|
||||||
Poll::Ready(Ok(buf)) => {
|
|
||||||
let len = buf.len();
|
|
||||||
n += len;
|
|
||||||
reader.as_mut().consume(len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
use super::super::error::Result;
|
|
||||||
use super::super::traits::AsyncWrite;
|
|
||||||
|
|
||||||
/// Future for the [`flush`](super::AsyncWriteExt::flush) method.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
||||||
pub struct Flush<'a, W: ?Sized> {
|
|
||||||
writer: &'a mut W,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: ?Sized + Unpin> Unpin for Flush<'_, W> {}
|
|
||||||
|
|
||||||
impl<'a, W: AsyncWrite + ?Sized + Unpin> Flush<'a, W> {
|
|
||||||
pub(super) fn new(writer: &'a mut W) -> Self {
|
|
||||||
Flush { writer }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: AsyncWrite + ?Sized + Unpin> Future for Flush<'_, W> {
|
|
||||||
type Output = Result<()>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
||||||
let this = &mut *self;
|
|
||||||
let _ = ready!(Pin::new(&mut this.writer).poll_flush(cx))?;
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
use core::cmp::min;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use core::task::{Context, Poll};
|
|
||||||
use futures::ready;
|
|
||||||
|
|
||||||
mod read;
|
|
||||||
pub use self::read::Read;
|
|
||||||
|
|
||||||
mod read_buf;
|
|
||||||
pub use self::read_buf::ReadBuf;
|
|
||||||
|
|
||||||
mod read_byte;
|
|
||||||
pub use self::read_byte::ReadByte;
|
|
||||||
|
|
||||||
mod read_exact;
|
|
||||||
pub use self::read_exact::ReadExact;
|
|
||||||
|
|
||||||
mod read_while;
|
|
||||||
pub use self::read_while::ReadWhile;
|
|
||||||
|
|
||||||
mod read_to_end;
|
|
||||||
pub use self::read_to_end::ReadToEnd;
|
|
||||||
|
|
||||||
mod skip_while;
|
|
||||||
pub use self::skip_while::SkipWhile;
|
|
||||||
|
|
||||||
mod drain;
|
|
||||||
pub use self::drain::Drain;
|
|
||||||
|
|
||||||
mod flush;
|
|
||||||
pub use self::flush::Flush;
|
|
||||||
|
|
||||||
mod write;
|
|
||||||
pub use self::write::Write;
|
|
||||||
|
|
||||||
mod write_all;
|
|
||||||
pub use self::write_all::WriteAll;
|
|
||||||
|
|
||||||
mod write_byte;
|
|
||||||
pub use self::write_byte::WriteByte;
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
mod split;
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
pub use self::split::{split, ReadHalf, WriteHalf};
|
|
||||||
|
|
||||||
mod copy_buf;
|
|
||||||
pub use self::copy_buf::{copy_buf, CopyBuf};
|
|
||||||
|
|
||||||
use super::error::Result;
|
|
||||||
use super::traits::{AsyncBufRead, AsyncWrite};
|
|
||||||
|
|
||||||
pub trait AsyncBufReadExt: AsyncBufRead {
|
|
||||||
fn poll_read(
|
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &mut [u8],
|
|
||||||
) -> Poll<Result<usize>>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
let mut this = &mut *self;
|
|
||||||
let rbuf = ready!(Pin::new(&mut this).poll_fill_buf(cx))?;
|
|
||||||
let n = min(buf.len(), rbuf.len());
|
|
||||||
buf[..n].copy_from_slice(&rbuf[..n]);
|
|
||||||
Pin::new(&mut this).consume(n);
|
|
||||||
Poll::Ready(Ok(n))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_while<'a, F: Fn(u8) -> bool>(
|
|
||||||
&'a mut self,
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
f: F,
|
|
||||||
) -> ReadWhile<'a, Self, F>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
ReadWhile::new(self, f, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skip_while<F: Fn(u8) -> bool>(&mut self, f: F) -> SkipWhile<Self, F>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
SkipWhile::new(self, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drain(&mut self) -> Drain<Self>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
Drain::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
Read::new(self, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_buf(&mut self) -> ReadBuf<Self>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
ReadBuf::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_byte(&mut self) -> ReadByte<Self>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
ReadByte::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
ReadExact::new(self, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_to_end<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadToEnd<'a, Self>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
ReadToEnd::new(self, buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: AsyncBufRead + ?Sized> AsyncBufReadExt for R {}
|
|
||||||
|
|
||||||
pub async fn read_line<R: AsyncBufRead + Unpin + ?Sized>(
|
|
||||||
r: &mut R,
|
|
||||||
buf: &mut [u8],
|
|
||||||
) -> Result<usize> {
|
|
||||||
r.skip_while(|b| b == b'\r' || b == b'\n').await?;
|
|
||||||
let n = r.read_while(buf, |b| b != b'\r' && b != b'\n').await?;
|
|
||||||
r.skip_while(|b| b == b'\r').await?;
|
|
||||||
//assert_eq!(b'\n', r.read_byte().await?);
|
|
||||||
r.read_byte().await?;
|
|
||||||
Ok(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AsyncWriteExt: AsyncWrite {
|
|
||||||
fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
WriteAll::new(self, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_byte(&mut self, byte: u8) -> WriteByte<Self>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
WriteByte::new(self, byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Write<'a, Self>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
Write::new(self, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Awaits until all bytes have actually been written, and
|
|
||||||
/// not just enqueued as per the other "write" methods.
|
|
||||||
fn flush<'a>(&mut self) -> Flush<Self>
|
|
||||||
where
|
|
||||||
Self: Unpin,
|
|
||||||
{
|
|
||||||
Flush::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: AsyncWrite + ?Sized> AsyncWriteExt for R {}
|
|
@ -1,39 +0,0 @@
|
|||||||
use super::super::error::Result;
|
|
||||||
use super::super::traits::AsyncBufRead;
|
|
||||||
|
|
||||||
use core::cmp::min;
|
|
||||||
|
|
||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
/// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
||||||
pub struct Read<'a, R: ?Sized> {
|
|
||||||
reader: &'a mut R,
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: ?Sized + Unpin> Unpin for Read<'_, R> {}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Read<'a, R> {
|
|
||||||
pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
|
|
||||||
Read { reader, buf }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: AsyncBufRead + ?Sized + Unpin> Future for Read<'_, R> {
|
|
||||||
type Output = Result<usize>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let this = &mut *self;
|
|
||||||
let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?;
|
|
||||||
|
|
||||||
let n = min(this.buf.len(), buf.len());
|
|
||||||
this.buf[..n].copy_from_slice(&buf[..n]);
|
|
||||||
Pin::new(&mut this.reader).consume(n);
|
|
||||||
Poll::Ready(Ok(n))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
use super::super::error::Result;
|
|
||||||
use super::super::traits::AsyncBufRead;
|
|
||||||
|
|
||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
pub struct ReadBuf<'a, R: ?Sized> {
|
|
||||||
reader: Option<&'a mut R>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: ?Sized + Unpin> Unpin for ReadBuf<'_, R> {}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadBuf<'a, R> {
|
|
||||||
pub(super) fn new(reader: &'a mut R) -> Self {
|
|
||||||
ReadBuf {
|
|
||||||
reader: Some(reader),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadBuf<'a, R> {
|
|
||||||
type Output = Result<&'a [u8]>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let this = &mut *self;
|
|
||||||
|
|
||||||
let buf = ready!(Pin::new(this.reader.as_mut().unwrap()).poll_fill_buf(cx))?;
|
|
||||||
let buf: &'a [u8] = unsafe { core::mem::transmute(buf) };
|
|
||||||
this.reader = None;
|
|
||||||
Poll::Ready(Ok(buf))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
use super::super::error::{Error, Result};
|
|
||||||
use super::super::traits::AsyncBufRead;
|
|
||||||
|
|
||||||
pub struct ReadByte<'a, R: ?Sized> {
|
|
||||||
reader: &'a mut R,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: ?Sized + Unpin> Unpin for ReadByte<'_, R> {}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadByte<'a, R> {
|
|
||||||
pub(super) fn new(reader: &'a mut R) -> Self {
|
|
||||||
Self { reader }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadByte<'a, R> {
|
|
||||||
type Output = Result<u8>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let Self { reader } = &mut *self;
|
|
||||||
let mut reader = Pin::new(reader);
|
|
||||||
let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
|
|
||||||
if rbuf.is_empty() {
|
|
||||||
return Poll::Ready(Err(Error::UnexpectedEof));
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = rbuf[0];
|
|
||||||
reader.as_mut().consume(1);
|
|
||||||
Poll::Ready(Ok(r))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
use super::super::error::{Error, Result};
|
|
||||||
use super::super::traits::AsyncBufRead;
|
|
||||||
|
|
||||||
use core::cmp::min;
|
|
||||||
use core::mem;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
/// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
||||||
pub struct ReadExact<'a, R: ?Sized> {
|
|
||||||
reader: &'a mut R,
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: ?Sized + Unpin> Unpin for ReadExact<'_, R> {}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadExact<'a, R> {
|
|
||||||
pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
|
|
||||||
ReadExact { reader, buf }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: AsyncBufRead + ?Sized + Unpin> Future for ReadExact<'_, R> {
|
|
||||||
type Output = Result<()>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let this = &mut *self;
|
|
||||||
while !this.buf.is_empty() {
|
|
||||||
let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?;
|
|
||||||
if buf.is_empty() {
|
|
||||||
return Poll::Ready(Err(Error::UnexpectedEof));
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = min(this.buf.len(), buf.len());
|
|
||||||
this.buf[..n].copy_from_slice(&buf[..n]);
|
|
||||||
Pin::new(&mut this.reader).consume(n);
|
|
||||||
{
|
|
||||||
let (_, rest) = mem::take(&mut this.buf).split_at_mut(n);
|
|
||||||
this.buf = rest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
use core::cmp::min;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
use super::super::error::{Error, Result};
|
|
||||||
use super::super::traits::AsyncBufRead;
|
|
||||||
|
|
||||||
pub struct ReadToEnd<'a, R: ?Sized> {
|
|
||||||
reader: &'a mut R,
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
n: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: ?Sized + Unpin> Unpin for ReadToEnd<'_, R> {}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadToEnd<'a, R> {
|
|
||||||
pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
|
|
||||||
Self { reader, buf, n: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadToEnd<'a, R> {
|
|
||||||
type Output = Result<usize>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let Self { reader, buf, n } = &mut *self;
|
|
||||||
let mut reader = Pin::new(reader);
|
|
||||||
loop {
|
|
||||||
let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
|
|
||||||
if rbuf.is_empty() {
|
|
||||||
return Poll::Ready(Ok(*n));
|
|
||||||
}
|
|
||||||
|
|
||||||
if *n == buf.len() {
|
|
||||||
return Poll::Ready(Err(Error::Truncated));
|
|
||||||
}
|
|
||||||
|
|
||||||
// truncate data if it doesn't fit in buf
|
|
||||||
let p = min(rbuf.len(), buf.len() - *n);
|
|
||||||
buf[*n..*n + p].copy_from_slice(&rbuf[..p]);
|
|
||||||
*n += p;
|
|
||||||
|
|
||||||
reader.as_mut().consume(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
use core::cmp::min;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
use super::super::error::{Error, Result};
|
|
||||||
use super::super::traits::AsyncBufRead;
|
|
||||||
|
|
||||||
pub struct ReadWhile<'a, R: ?Sized, F> {
|
|
||||||
reader: &'a mut R,
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
n: usize,
|
|
||||||
f: F,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: ?Sized + Unpin, F> Unpin for ReadWhile<'_, R, F> {}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> ReadWhile<'a, R, F> {
|
|
||||||
pub(super) fn new(reader: &'a mut R, f: F, buf: &'a mut [u8]) -> Self {
|
|
||||||
Self {
|
|
||||||
reader,
|
|
||||||
f,
|
|
||||||
buf,
|
|
||||||
n: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for ReadWhile<'a, R, F> {
|
|
||||||
type Output = Result<usize>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let Self { reader, f, buf, n } = &mut *self;
|
|
||||||
let mut reader = Pin::new(reader);
|
|
||||||
loop {
|
|
||||||
let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
|
|
||||||
if rbuf.is_empty() {
|
|
||||||
return Poll::Ready(Err(Error::UnexpectedEof));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (p, done) = match rbuf.iter().position(|&b| !f(b)) {
|
|
||||||
Some(p) => (p, true),
|
|
||||||
None => (rbuf.len(), false),
|
|
||||||
};
|
|
||||||
|
|
||||||
// truncate data if it doesn't fit in buf
|
|
||||||
let p2 = min(p, buf.len() - *n);
|
|
||||||
buf[*n..*n + p2].copy_from_slice(&rbuf[..p2]);
|
|
||||||
*n += p2;
|
|
||||||
|
|
||||||
// consume it all, even if it doesn't fit.
|
|
||||||
// Otherwise we can deadlock because we never read to the ending char
|
|
||||||
reader.as_mut().consume(p);
|
|
||||||
|
|
||||||
if done {
|
|
||||||
return Poll::Ready(Ok(*n));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
use core::iter::Iterator;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
use super::super::error::{Error, Result};
|
|
||||||
use super::super::traits::AsyncBufRead;
|
|
||||||
|
|
||||||
pub struct SkipWhile<'a, R: ?Sized, F> {
|
|
||||||
reader: &'a mut R,
|
|
||||||
f: F,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: ?Sized + Unpin, F> Unpin for SkipWhile<'_, R, F> {}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> SkipWhile<'a, R, F> {
|
|
||||||
pub(super) fn new(reader: &'a mut R, f: F) -> Self {
|
|
||||||
Self { reader, f }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for SkipWhile<'a, R, F> {
|
|
||||||
type Output = Result<()>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let Self { reader, f } = &mut *self;
|
|
||||||
let mut reader = Pin::new(reader);
|
|
||||||
loop {
|
|
||||||
let buf = ready!(reader.as_mut().poll_fill_buf(cx))?;
|
|
||||||
if buf.is_empty() {
|
|
||||||
return Poll::Ready(Err(Error::UnexpectedEof));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (p, done) = match buf.iter().position(|b| !f(*b)) {
|
|
||||||
Some(p) => (p, true),
|
|
||||||
None => (buf.len(), false),
|
|
||||||
};
|
|
||||||
reader.as_mut().consume(p);
|
|
||||||
if done {
|
|
||||||
return Poll::Ready(Ok(()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
use alloc::rc::Rc;
|
|
||||||
use core::cell::UnsafeCell;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
use super::super::error::Result;
|
|
||||||
use super::super::traits::{AsyncBufRead, AsyncWrite};
|
|
||||||
|
|
||||||
/// The readable half of an object returned from `AsyncBufRead::split`.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ReadHalf<T> {
|
|
||||||
handle: Rc<UnsafeCell<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The writable half of an object returned from `AsyncBufRead::split`.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct WriteHalf<T> {
|
|
||||||
handle: Rc<UnsafeCell<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsyncBufRead + Unpin> AsyncBufRead for ReadHalf<T> {
|
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
|
|
||||||
Pin::new(unsafe { &mut *self.handle.get() }).poll_fill_buf(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
||||||
Pin::new(unsafe { &mut *self.handle.get() }).consume(amt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsyncWrite + Unpin> AsyncWrite for WriteHalf<T> {
|
|
||||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
|
|
||||||
Pin::new(unsafe { &mut *self.handle.get() }).poll_write(cx, buf)
|
|
||||||
}
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
||||||
Pin::new(unsafe { &mut *self.handle.get() }).poll_flush(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn split<T: AsyncBufRead + AsyncWrite>(t: T) -> (ReadHalf<T>, WriteHalf<T>) {
|
|
||||||
let c = Rc::new(UnsafeCell::new(t));
|
|
||||||
(ReadHalf { handle: c.clone() }, WriteHalf { handle: c })
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
use super::super::error::Result;
|
|
||||||
use super::super::traits::AsyncWrite;
|
|
||||||
|
|
||||||
/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
||||||
pub struct Write<'a, W: ?Sized> {
|
|
||||||
writer: &'a mut W,
|
|
||||||
buf: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: ?Sized + Unpin> Unpin for Write<'_, W> {}
|
|
||||||
|
|
||||||
impl<'a, W: AsyncWrite + ?Sized + Unpin> Write<'a, W> {
|
|
||||||
pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self {
|
|
||||||
Write { writer, buf }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: AsyncWrite + ?Sized + Unpin> Future for Write<'_, W> {
|
|
||||||
type Output = Result<usize>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<usize>> {
|
|
||||||
let this = &mut *self;
|
|
||||||
let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?;
|
|
||||||
Poll::Ready(Ok(n))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
use core::mem;
|
|
||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
use super::super::error::Result;
|
|
||||||
use super::super::traits::AsyncWrite;
|
|
||||||
|
|
||||||
/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
||||||
pub struct WriteAll<'a, W: ?Sized> {
|
|
||||||
writer: &'a mut W,
|
|
||||||
buf: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: ?Sized + Unpin> Unpin for WriteAll<'_, W> {}
|
|
||||||
|
|
||||||
impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteAll<'a, W> {
|
|
||||||
pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self {
|
|
||||||
WriteAll { writer, buf }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: AsyncWrite + ?Sized + Unpin> Future for WriteAll<'_, W> {
|
|
||||||
type Output = Result<()>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
||||||
let this = &mut *self;
|
|
||||||
while !this.buf.is_empty() {
|
|
||||||
let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?;
|
|
||||||
{
|
|
||||||
let (_, rest) = mem::take(&mut this.buf).split_at(n);
|
|
||||||
this.buf = rest;
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
use core::pin::Pin;
|
|
||||||
use futures::future::Future;
|
|
||||||
use futures::ready;
|
|
||||||
use futures::task::{Context, Poll};
|
|
||||||
|
|
||||||
use super::super::error::Result;
|
|
||||||
use super::super::traits::AsyncWrite;
|
|
||||||
|
|
||||||
/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
||||||
pub struct WriteByte<'a, W: ?Sized> {
|
|
||||||
writer: &'a mut W,
|
|
||||||
byte: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: ?Sized + Unpin> Unpin for WriteByte<'_, W> {}
|
|
||||||
|
|
||||||
impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteByte<'a, W> {
|
|
||||||
pub(super) fn new(writer: &'a mut W, byte: u8) -> Self {
|
|
||||||
WriteByte { writer, byte }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: AsyncWrite + ?Sized + Unpin> Future for WriteByte<'_, W> {
|
|
||||||
type Output = Result<()>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
|
||||||
let this = &mut *self;
|
|
||||||
let buf = [this.byte; 1];
|
|
||||||
let n = ready!(Pin::new(&mut this.writer).poll_write(cx, &buf))?;
|
|
||||||
if n == 0 {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
assert!(n == 1);
|
|
||||||
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,7 +13,6 @@ pub mod channel;
|
|||||||
pub mod executor;
|
pub mod executor;
|
||||||
#[cfg(cortex_m)]
|
#[cfg(cortex_m)]
|
||||||
pub mod interrupt;
|
pub mod interrupt;
|
||||||
pub mod io;
|
|
||||||
pub mod mutex;
|
pub mod mutex;
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
6
examples/boot/stm32f3/.cargo/config.toml
Normal file
6
examples/boot/stm32f3/.cargo/config.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
|
||||||
|
runner = "probe-run --chip STM32F303VCTx"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7em-none-eabihf"
|
26
examples/boot/stm32f3/Cargo.toml
Normal file
26
examples/boot/stm32f3/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Ulf Lilleengen <lulf@redhat.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
name = "embassy-boot-stm32f3-examples"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
|
||||||
|
embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] }
|
||||||
|
embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
|
||||||
|
embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
|
||||||
|
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
defmt-rtt = { version = "0.3", optional = true }
|
||||||
|
panic-reset = { version = "0.1.1" }
|
||||||
|
embedded-hal = { version = "0.2.6" }
|
||||||
|
|
||||||
|
cortex-m = "0.7.3"
|
||||||
|
cortex-m-rt = "0.7.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
defmt = [
|
||||||
|
"dep:defmt",
|
||||||
|
"embassy-stm32/defmt",
|
||||||
|
"embassy-boot-stm32/defmt",
|
||||||
|
]
|
29
examples/boot/stm32f3/README.md
Normal file
29
examples/boot/stm32f3/README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Examples using bootloader
|
||||||
|
|
||||||
|
Example for STM32F3 demonstrating the bootloader. The example consists of application binaries, 'a'
|
||||||
|
which allows you to press a button to start the DFU process, and 'b' which is the updated
|
||||||
|
application.
|
||||||
|
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
* `cargo-binutils`
|
||||||
|
* `cargo-flash`
|
||||||
|
* `embassy-boot-stm32`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
# Flash bootloader
|
||||||
|
cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f303re --chip STM32F303RETx
|
||||||
|
# Build 'b'
|
||||||
|
cargo build --release --bin b
|
||||||
|
# Generate binary for 'b'
|
||||||
|
cargo objcopy --release --bin b -- -O binary b.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
# Flash `a` (which includes b.bin)
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo flash --release --bin a --chip STM32F303RETx
|
||||||
|
```
|
37
examples/boot/stm32f3/build.rs
Normal file
37
examples/boot/stm32f3/build.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//! This build script copies the `memory.x` file from the crate root into
|
||||||
|
//! a directory where the linker can always find it at build time.
|
||||||
|
//! For many projects this is optional, as the linker always searches the
|
||||||
|
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||||
|
//! are using a workspace or have a more complicated build setup, this
|
||||||
|
//! build script becomes required. Additionally, by requesting that
|
||||||
|
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||||
|
//! updating `memory.x` ensures a rebuild of the application with the
|
||||||
|
//! new memory settings.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Put `memory.x` in our output directory and ensure it's
|
||||||
|
// on the linker search path.
|
||||||
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
File::create(out.join("memory.x"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(include_bytes!("memory.x"))
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
|
// By default, Cargo will re-run a build script whenever
|
||||||
|
// any file in the project changes. By specifying `memory.x`
|
||||||
|
// here, we ensure the build script is only re-run when
|
||||||
|
// `memory.x` is changed.
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||||
|
if env::var("CARGO_FEATURE_DEFMT").is_ok() {
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||||
|
}
|
||||||
|
}
|
15
examples/boot/stm32f3/memory.x
Normal file
15
examples/boot/stm32f3/memory.x
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
|
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
|
||||||
|
BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
|
||||||
|
FLASH : ORIGIN = 0x08008000, LENGTH = 32K
|
||||||
|
DFU : ORIGIN = 0x08010000, LENGTH = 36K
|
||||||
|
RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K
|
||||||
|
}
|
||||||
|
|
||||||
|
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
|
||||||
|
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
|
||||||
|
|
||||||
|
__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
|
||||||
|
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
|
44
examples/boot/stm32f3/src/bin/a.rs
Normal file
44
examples/boot/stm32f3/src/bin/a.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use embassy_boot_stm32::FirmwareUpdater;
|
||||||
|
use embassy_stm32::exti::ExtiInput;
|
||||||
|
use embassy_stm32::flash::Flash;
|
||||||
|
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embassy_traits::adapter::BlockingAsync;
|
||||||
|
use panic_reset as _;
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt-rtt")]
|
||||||
|
use defmt_rtt::*;
|
||||||
|
|
||||||
|
static APP_B: &[u8] = include_bytes!("../../b.bin");
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
|
||||||
|
let flash = Flash::unlock(p.FLASH);
|
||||||
|
let mut flash = BlockingAsync::new(flash);
|
||||||
|
|
||||||
|
let button = Input::new(p.PC13, Pull::Up);
|
||||||
|
let mut button = ExtiInput::new(button, p.EXTI13);
|
||||||
|
|
||||||
|
let mut led = Output::new(p.PA5, Level::Low, Speed::Low);
|
||||||
|
led.set_high();
|
||||||
|
|
||||||
|
let mut updater = FirmwareUpdater::default();
|
||||||
|
button.wait_for_falling_edge().await;
|
||||||
|
let mut offset = 0;
|
||||||
|
for chunk in APP_B.chunks(2048) {
|
||||||
|
let mut buf: [u8; 2048] = [0; 2048];
|
||||||
|
buf[..chunk.len()].copy_from_slice(chunk);
|
||||||
|
updater
|
||||||
|
.write_firmware(offset, &buf, &mut flash, 2048)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
offset += chunk.len();
|
||||||
|
}
|
||||||
|
updater.update(&mut flash).await.unwrap();
|
||||||
|
led.set_low();
|
||||||
|
cortex_m::peripheral::SCB::sys_reset();
|
||||||
|
}
|
25
examples/boot/stm32f3/src/bin/b.rs
Normal file
25
examples/boot/stm32f3/src/bin/b.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::time::{Duration, Timer};
|
||||||
|
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use panic_reset as _;
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt-rtt")]
|
||||||
|
use defmt_rtt::*;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
let mut led = Output::new(p.PA5, Level::High, Speed::Low);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
led.set_high();
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
|
||||||
|
led.set_low();
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
}
|
6
examples/boot/stm32f7/.cargo/config.toml
Normal file
6
examples/boot/stm32f7/.cargo/config.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
|
||||||
|
runner = "probe-run --chip STM32F767ZITx -v"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7em-none-eabihf"
|
26
examples/boot/stm32f7/Cargo.toml
Normal file
26
examples/boot/stm32f7/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Ulf Lilleengen <lulf@redhat.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
name = "embassy-boot-stm32f7-examples"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
|
||||||
|
embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] }
|
||||||
|
embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
|
||||||
|
embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
|
||||||
|
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
defmt-rtt = { version = "0.3", optional = true }
|
||||||
|
panic-reset = { version = "0.1.1" }
|
||||||
|
embedded-hal = { version = "0.2.6" }
|
||||||
|
|
||||||
|
cortex-m = "0.7.3"
|
||||||
|
cortex-m-rt = "0.7.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
defmt = [
|
||||||
|
"dep:defmt",
|
||||||
|
"embassy-stm32/defmt",
|
||||||
|
"embassy-boot-stm32/defmt",
|
||||||
|
]
|
29
examples/boot/stm32f7/README.md
Normal file
29
examples/boot/stm32f7/README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Examples using bootloader
|
||||||
|
|
||||||
|
Example for STM32F7 demonstrating the bootloader. The example consists of application binaries, 'a'
|
||||||
|
which allows you to press a button to start the DFU process, and 'b' which is the updated
|
||||||
|
application.
|
||||||
|
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
* `cargo-binutils`
|
||||||
|
* `cargo-flash`
|
||||||
|
* `embassy-boot-stm32`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
# Flash bootloader
|
||||||
|
./flash-boot.sh
|
||||||
|
# Build 'b'
|
||||||
|
cargo build --release --bin b
|
||||||
|
# Generate binary for 'b'
|
||||||
|
cargo objcopy --release --bin b -- -O binary b.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
# Flash `a` (which includes b.bin)
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo flash --release --bin a --chip STM32F767ZITx
|
||||||
|
```
|
37
examples/boot/stm32f7/build.rs
Normal file
37
examples/boot/stm32f7/build.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//! This build script copies the `memory.x` file from the crate root into
|
||||||
|
//! a directory where the linker can always find it at build time.
|
||||||
|
//! For many projects this is optional, as the linker always searches the
|
||||||
|
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||||
|
//! are using a workspace or have a more complicated build setup, this
|
||||||
|
//! build script becomes required. Additionally, by requesting that
|
||||||
|
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||||
|
//! updating `memory.x` ensures a rebuild of the application with the
|
||||||
|
//! new memory settings.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Put `memory.x` in our output directory and ensure it's
|
||||||
|
// on the linker search path.
|
||||||
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
File::create(out.join("memory.x"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(include_bytes!("memory.x"))
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
|
// By default, Cargo will re-run a build script whenever
|
||||||
|
// any file in the project changes. By specifying `memory.x`
|
||||||
|
// here, we ensure the build script is only re-run when
|
||||||
|
// `memory.x` is changed.
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||||
|
if env::var("CARGO_FEATURE_DEFMT").is_ok() {
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||||
|
}
|
||||||
|
}
|
8
examples/boot/stm32f7/flash-boot.sh
Executable file
8
examples/boot/stm32f7/flash-boot.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
mv ../../../embassy-boot/stm32/memory.x ../../../embassy-boot/stm32/memory-old.x
|
||||||
|
cp memory-bl.x ../../../embassy-boot/stm32/memory.x
|
||||||
|
|
||||||
|
cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32F767ZITx --target thumbv7em-none-eabihf
|
||||||
|
|
||||||
|
rm ../../../embassy-boot/stm32/memory.x
|
||||||
|
mv ../../../embassy-boot/stm32/memory-old.x ../../../embassy-boot/stm32/memory.x
|
18
examples/boot/stm32f7/memory-bl.x
Normal file
18
examples/boot/stm32f7/memory-bl.x
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 256K
|
||||||
|
BOOTLOADER_STATE : ORIGIN = 0x08040000, LENGTH = 256K
|
||||||
|
ACTIVE : ORIGIN = 0x08080000, LENGTH = 256K
|
||||||
|
DFU : ORIGIN = 0x080c0000, LENGTH = 512K
|
||||||
|
RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 368K + 16K
|
||||||
|
}
|
||||||
|
|
||||||
|
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);
|
||||||
|
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);
|
||||||
|
|
||||||
|
__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);
|
||||||
|
__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);
|
||||||
|
|
||||||
|
__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);
|
||||||
|
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);
|
15
examples/boot/stm32f7/memory.x
Normal file
15
examples/boot/stm32f7/memory.x
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
|
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 256K
|
||||||
|
BOOTLOADER_STATE : ORIGIN = 0x08040000, LENGTH = 256K
|
||||||
|
FLASH : ORIGIN = 0x08080000, LENGTH = 256K
|
||||||
|
DFU : ORIGIN = 0x080c0000, LENGTH = 512K
|
||||||
|
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 368K + 16K
|
||||||
|
}
|
||||||
|
|
||||||
|
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
|
||||||
|
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
|
||||||
|
|
||||||
|
__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
|
||||||
|
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
|
44
examples/boot/stm32f7/src/bin/a.rs
Normal file
44
examples/boot/stm32f7/src/bin/a.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use embassy_boot_stm32::FirmwareUpdater;
|
||||||
|
use embassy_stm32::exti::ExtiInput;
|
||||||
|
use embassy_stm32::flash::Flash;
|
||||||
|
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embassy_traits::adapter::BlockingAsync;
|
||||||
|
use panic_reset as _;
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt-rtt")]
|
||||||
|
use defmt_rtt::*;
|
||||||
|
|
||||||
|
static APP_B: &[u8] = include_bytes!("../../b.bin");
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
|
||||||
|
let flash = Flash::unlock(p.FLASH);
|
||||||
|
let mut flash = BlockingAsync::new(flash);
|
||||||
|
|
||||||
|
let button = Input::new(p.PC13, Pull::Down);
|
||||||
|
let mut button = ExtiInput::new(button, p.EXTI13);
|
||||||
|
|
||||||
|
let mut led = Output::new(p.PB7, Level::Low, Speed::Low);
|
||||||
|
led.set_high();
|
||||||
|
|
||||||
|
let mut updater = FirmwareUpdater::default();
|
||||||
|
button.wait_for_rising_edge().await;
|
||||||
|
let mut offset = 0;
|
||||||
|
let mut buf: [u8; 256 * 1024] = [0; 256 * 1024];
|
||||||
|
for chunk in APP_B.chunks(256 * 1024) {
|
||||||
|
buf[..chunk.len()].copy_from_slice(chunk);
|
||||||
|
updater
|
||||||
|
.write_firmware(offset, &buf, &mut flash, 2048)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
offset += chunk.len();
|
||||||
|
}
|
||||||
|
updater.update(&mut flash).await.unwrap();
|
||||||
|
led.set_low();
|
||||||
|
cortex_m::peripheral::SCB::sys_reset();
|
||||||
|
}
|
27
examples/boot/stm32f7/src/bin/b.rs
Normal file
27
examples/boot/stm32f7/src/bin/b.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::time::{Duration, Timer};
|
||||||
|
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use panic_reset as _;
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt-rtt")]
|
||||||
|
use defmt_rtt::*;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
Timer::after(Duration::from_millis(300)).await;
|
||||||
|
let mut led = Output::new(p.PB7, Level::High, Speed::Low);
|
||||||
|
led.set_high();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
led.set_high();
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
|
||||||
|
led.set_low();
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
}
|
6
examples/boot/stm32h7/.cargo/config.toml
Normal file
6
examples/boot/stm32h7/.cargo/config.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
|
||||||
|
runner = "probe-run --chip STM32H743ZITx"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7em-none-eabihf"
|
26
examples/boot/stm32h7/Cargo.toml
Normal file
26
examples/boot/stm32h7/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Ulf Lilleengen <lulf@redhat.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
name = "embassy-boot-stm32f7-examples"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
|
||||||
|
embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] }
|
||||||
|
embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
|
||||||
|
embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
|
||||||
|
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
defmt-rtt = { version = "0.3", optional = true }
|
||||||
|
panic-reset = { version = "0.1.1" }
|
||||||
|
embedded-hal = { version = "0.2.6" }
|
||||||
|
|
||||||
|
cortex-m = "0.7.3"
|
||||||
|
cortex-m-rt = "0.7.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
defmt = [
|
||||||
|
"dep:defmt",
|
||||||
|
"embassy-stm32/defmt",
|
||||||
|
"embassy-boot-stm32/defmt",
|
||||||
|
]
|
29
examples/boot/stm32h7/README.md
Normal file
29
examples/boot/stm32h7/README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Examples using bootloader
|
||||||
|
|
||||||
|
Example for STM32H7 demonstrating the bootloader. The example consists of application binaries, 'a'
|
||||||
|
which allows you to press a button to start the DFU process, and 'b' which is the updated
|
||||||
|
application.
|
||||||
|
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
* `cargo-binutils`
|
||||||
|
* `cargo-flash`
|
||||||
|
* `embassy-boot-stm32`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
# Flash bootloader
|
||||||
|
./flash-boot.sh
|
||||||
|
# Build 'b'
|
||||||
|
cargo build --release --bin b
|
||||||
|
# Generate binary for 'b'
|
||||||
|
cargo objcopy --release --bin b -- -O binary b.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
# Flash `a` (which includes b.bin)
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo flash --release --bin a --chip STM32H743ZITx
|
||||||
|
```
|
37
examples/boot/stm32h7/build.rs
Normal file
37
examples/boot/stm32h7/build.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//! This build script copies the `memory.x` file from the crate root into
|
||||||
|
//! a directory where the linker can always find it at build time.
|
||||||
|
//! For many projects this is optional, as the linker always searches the
|
||||||
|
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||||
|
//! are using a workspace or have a more complicated build setup, this
|
||||||
|
//! build script becomes required. Additionally, by requesting that
|
||||||
|
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||||
|
//! updating `memory.x` ensures a rebuild of the application with the
|
||||||
|
//! new memory settings.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Put `memory.x` in our output directory and ensure it's
|
||||||
|
// on the linker search path.
|
||||||
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
File::create(out.join("memory.x"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(include_bytes!("memory.x"))
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
|
// By default, Cargo will re-run a build script whenever
|
||||||
|
// any file in the project changes. By specifying `memory.x`
|
||||||
|
// here, we ensure the build script is only re-run when
|
||||||
|
// `memory.x` is changed.
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||||
|
if env::var("CARGO_FEATURE_DEFMT").is_ok() {
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||||
|
}
|
||||||
|
}
|
8
examples/boot/stm32h7/flash-boot.sh
Executable file
8
examples/boot/stm32h7/flash-boot.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
mv ../../../embassy-boot/stm32/memory.x ../../../embassy-boot/stm32/memory-old.x
|
||||||
|
cp memory-bl.x ../../../embassy-boot/stm32/memory.x
|
||||||
|
|
||||||
|
cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32H743ZITx --target thumbv7em-none-eabihf
|
||||||
|
|
||||||
|
rm ../../../embassy-boot/stm32/memory.x
|
||||||
|
mv ../../../embassy-boot/stm32/memory-old.x ../../../embassy-boot/stm32/memory.x
|
18
examples/boot/stm32h7/memory-bl.x
Normal file
18
examples/boot/stm32h7/memory-bl.x
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 128K
|
||||||
|
BOOTLOADER_STATE : ORIGIN = 0x08020000, LENGTH = 128K
|
||||||
|
ACTIVE : ORIGIN = 0x08040000, LENGTH = 128K
|
||||||
|
DFU : ORIGIN = 0x08100000, LENGTH = 512K
|
||||||
|
RAM (rwx) : ORIGIN = 0x24000000, LENGTH = 368K
|
||||||
|
}
|
||||||
|
|
||||||
|
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);
|
||||||
|
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);
|
||||||
|
|
||||||
|
__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);
|
||||||
|
__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);
|
||||||
|
|
||||||
|
__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);
|
||||||
|
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);
|
15
examples/boot/stm32h7/memory.x
Normal file
15
examples/boot/stm32h7/memory.x
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
|
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 128K
|
||||||
|
BOOTLOADER_STATE : ORIGIN = 0x08020000, LENGTH = 128K
|
||||||
|
FLASH : ORIGIN = 0x08040000, LENGTH = 256K
|
||||||
|
DFU : ORIGIN = 0x08100000, LENGTH = 512K
|
||||||
|
RAM (rwx) : ORIGIN = 0x24000000, LENGTH = 368K
|
||||||
|
}
|
||||||
|
|
||||||
|
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
|
||||||
|
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
|
||||||
|
|
||||||
|
__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
|
||||||
|
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
|
44
examples/boot/stm32h7/src/bin/a.rs
Normal file
44
examples/boot/stm32h7/src/bin/a.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use embassy_boot_stm32::FirmwareUpdater;
|
||||||
|
use embassy_stm32::exti::ExtiInput;
|
||||||
|
use embassy_stm32::flash::Flash;
|
||||||
|
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embassy_traits::adapter::BlockingAsync;
|
||||||
|
use panic_reset as _;
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt-rtt")]
|
||||||
|
use defmt_rtt::*;
|
||||||
|
|
||||||
|
static APP_B: &[u8] = include_bytes!("../../b.bin");
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
|
||||||
|
let flash = Flash::unlock(p.FLASH);
|
||||||
|
let mut flash = BlockingAsync::new(flash);
|
||||||
|
|
||||||
|
let button = Input::new(p.PC13, Pull::Down);
|
||||||
|
let mut button = ExtiInput::new(button, p.EXTI13);
|
||||||
|
|
||||||
|
let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
|
||||||
|
led.set_high();
|
||||||
|
|
||||||
|
let mut updater = FirmwareUpdater::default();
|
||||||
|
button.wait_for_rising_edge().await;
|
||||||
|
let mut offset = 0;
|
||||||
|
let mut buf: [u8; 128 * 1024] = [0; 128 * 1024];
|
||||||
|
for chunk in APP_B.chunks(128 * 1024) {
|
||||||
|
buf[..chunk.len()].copy_from_slice(chunk);
|
||||||
|
updater
|
||||||
|
.write_firmware(offset, &buf, &mut flash, 2048)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
offset += chunk.len();
|
||||||
|
}
|
||||||
|
updater.update(&mut flash).await.unwrap();
|
||||||
|
led.set_low();
|
||||||
|
cortex_m::peripheral::SCB::sys_reset();
|
||||||
|
}
|
27
examples/boot/stm32h7/src/bin/b.rs
Normal file
27
examples/boot/stm32h7/src/bin/b.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::time::{Duration, Timer};
|
||||||
|
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use panic_reset as _;
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt-rtt")]
|
||||||
|
use defmt_rtt::*;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
Timer::after(Duration::from_millis(300)).await;
|
||||||
|
let mut led = Output::new(p.PB14, Level::High, Speed::Low);
|
||||||
|
led.set_high();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
led.set_high();
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
|
||||||
|
led.set_low();
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["nightly"]
|
default = ["nightly"]
|
||||||
nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm"]
|
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"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
|
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
|
|||||||
embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true }
|
embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true }
|
||||||
embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true }
|
embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true }
|
||||||
embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true }
|
embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true }
|
||||||
|
embedded-io = "0.2.0"
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy::executor::Spawner;
|
use embassy::executor::Spawner;
|
||||||
use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
|
|
||||||
use embassy_nrf::buffered_uarte::State;
|
use embassy_nrf::buffered_uarte::State;
|
||||||
use embassy_nrf::{buffered_uarte::BufferedUarte, interrupt, uarte, Peripherals};
|
use embassy_nrf::{buffered_uarte::BufferedUarte, interrupt, uarte, Peripherals};
|
||||||
|
use embedded_io::asynch::{Read, Write};
|
||||||
use futures::pin_mut;
|
use futures::pin_mut;
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
use defmt_rtt as _; // global logger
|
||||||
|
@ -10,9 +10,9 @@ use defmt::*;
|
|||||||
use embassy::blocking_mutex::raw::ThreadModeRawMutex;
|
use embassy::blocking_mutex::raw::ThreadModeRawMutex;
|
||||||
use embassy::channel::Channel;
|
use embassy::channel::Channel;
|
||||||
use embassy::executor::Spawner;
|
use embassy::executor::Spawner;
|
||||||
use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
|
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, TcpSocket};
|
use embassy_net::tcp::TcpSocket;
|
||||||
|
use embassy_net::{PacketBox, PacketBoxExt, PacketBuf};
|
||||||
use embassy_nrf::pac;
|
use embassy_nrf::pac;
|
||||||
use embassy_nrf::usb::Driver;
|
use embassy_nrf::usb::Driver;
|
||||||
use embassy_nrf::Peripherals;
|
use embassy_nrf::Peripherals;
|
||||||
@ -20,7 +20,9 @@ use embassy_nrf::{interrupt, peripherals};
|
|||||||
use embassy_usb::{Builder, Config, UsbDevice};
|
use embassy_usb::{Builder, Config, UsbDevice};
|
||||||
use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
|
use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
use defmt_rtt as _;
|
||||||
|
use embedded_io::asynch::{Read, Write};
|
||||||
|
// global logger
|
||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
|
|
||||||
type MyDriver = Driver<'static, peripherals::USBD>;
|
type MyDriver = Driver<'static, peripherals::USBD>;
|
||||||
|
@ -6,7 +6,8 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] }
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time", "nightly"] }
|
||||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] }
|
embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["nightly", "std", "log", "medium-ethernet", "tcp", "dhcpv4", "pool-16"] }
|
||||||
|
embedded-io = { version = "0.2.0", features = ["async", "std"] }
|
||||||
|
|
||||||
async-io = "1.6.0"
|
async-io = "1.6.0"
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
|
@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use embassy::executor::{Executor, Spawner};
|
use embassy::executor::{Executor, Spawner};
|
||||||
use embassy::io::AsyncWriteExt;
|
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
|
use embassy_net::tcp::TcpSocket;
|
||||||
use embassy_net::{
|
use embassy_net::{
|
||||||
Config, Configurator, DhcpConfigurator, Ipv4Address, Ipv4Cidr, StackResources,
|
Config, Configurator, DhcpConfigurator, Ipv4Address, Ipv4Cidr, StackResources,
|
||||||
StaticConfigurator, TcpSocket,
|
StaticConfigurator,
|
||||||
};
|
};
|
||||||
|
use embedded_io::asynch::Write;
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@ mod serial_port;
|
|||||||
|
|
||||||
use async_io::Async;
|
use async_io::Async;
|
||||||
use embassy::executor::Executor;
|
use embassy::executor::Executor;
|
||||||
use embassy::io::AsyncBufReadExt;
|
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
|
use embedded_io::asynch::Read;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nix::sys::termios;
|
use nix::sys::termios;
|
||||||
|
|
||||||
@ -24,12 +24,12 @@ async fn run() {
|
|||||||
// Essentially, async_io::Async converts from AsRawFd+Read+Write to futures's AsyncRead+AsyncWrite
|
// Essentially, async_io::Async converts from AsRawFd+Read+Write to futures's AsyncRead+AsyncWrite
|
||||||
let port = Async::new(port).unwrap();
|
let port = Async::new(port).unwrap();
|
||||||
|
|
||||||
// This implements futures's AsyncBufRead based on futures's AsyncRead
|
// We can then use FromStdIo to convert from futures's AsyncRead+AsyncWrite
|
||||||
let port = futures::io::BufReader::new(port);
|
// to embedded_io's async Read+Write.
|
||||||
|
//
|
||||||
// We can then use FromStdIo to convert from futures's AsyncBufRead+AsyncWrite
|
// This is not really needed, you could write the code below using futures::io directly.
|
||||||
// to embassy's AsyncBufRead+AsyncWrite
|
// It's useful if you want to have portable code across embedded and std.
|
||||||
let mut port = embassy::io::FromStdIo::new(port);
|
let mut port = embedded_io::adapters::FromFutures::new(port);
|
||||||
|
|
||||||
info!("Serial opened!");
|
info!("Serial opened!");
|
||||||
|
|
||||||
|
@ -19,3 +19,4 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
|
|||||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||||
heapless = { version = "0.7.5", default-features = false }
|
heapless = { version = "0.7.5", default-features = false }
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
|
embedded-storage = "0.3.0"
|
||||||
|
@ -15,7 +15,7 @@ use panic_probe as _;
|
|||||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
let mut led = Output::new(p.PE12, Level::High, Speed::Low);
|
let mut led = Output::new(p.PA5, Level::High, Speed::Low);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
info!("high");
|
info!("high");
|
||||||
|
43
examples/stm32f3/src/bin/flash.rs
Normal file
43
examples/stm32f3/src/bin/flash.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::{info, unwrap};
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy_stm32::flash::Flash;
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
|
||||||
|
|
||||||
|
use defmt_rtt as _; // global logger
|
||||||
|
use panic_probe as _;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
info!("Hello Flash!");
|
||||||
|
|
||||||
|
const ADDR: u32 = 0x26000;
|
||||||
|
|
||||||
|
let mut f = Flash::unlock(p.FLASH);
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
let mut buf = [0u8; 8];
|
||||||
|
unwrap!(f.read(ADDR, &mut buf));
|
||||||
|
info!("Read: {=[u8]:x}", buf);
|
||||||
|
|
||||||
|
info!("Erasing...");
|
||||||
|
unwrap!(f.erase(ADDR, ADDR + 2048));
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
let mut buf = [0u8; 8];
|
||||||
|
unwrap!(f.read(ADDR, &mut buf));
|
||||||
|
info!("Read after erase: {=[u8]:x}", buf);
|
||||||
|
|
||||||
|
info!("Writing...");
|
||||||
|
unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
let mut buf = [0u8; 8];
|
||||||
|
unwrap!(f.read(ADDR, &mut buf));
|
||||||
|
info!("Read: {=[u8]:x}", buf);
|
||||||
|
assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);
|
||||||
|
}
|
@ -8,7 +8,7 @@ resolver = "2"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] }
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] }
|
||||||
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"] }
|
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
#![feature(type_alias_impl_trait)]
|
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use panic_probe as _;
|
|
||||||
|
|
||||||
use defmt::{info, unwrap};
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use embassy::interrupt::InterruptExt;
|
|
||||||
use futures::pin_mut;
|
|
||||||
use panic_probe as _; // print out panic messages
|
|
||||||
|
|
||||||
use embassy::executor::Spawner;
|
|
||||||
use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
|
|
||||||
use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
|
|
||||||
use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals};
|
|
||||||
use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
|
|
||||||
|
|
||||||
static mut EP_MEMORY: [u32; 2048] = [0; 2048];
|
|
||||||
|
|
||||||
// USB requires at least 48 MHz clock
|
|
||||||
fn config() -> Config {
|
|
||||||
let mut config = Config::default();
|
|
||||||
config.rcc.sys_ck = Some(Hertz(48_000_000));
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy::main(config = "config()")]
|
|
||||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
|
||||||
let mut rx_buffer = [0u8; 64];
|
|
||||||
// we send back input + cr + lf
|
|
||||||
let mut tx_buffer = [0u8; 66];
|
|
||||||
|
|
||||||
let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11);
|
|
||||||
let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY });
|
|
||||||
|
|
||||||
let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
|
|
||||||
|
|
||||||
let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
|
|
||||||
.manufacturer("Fake company")
|
|
||||||
.product("Serial port")
|
|
||||||
.serial_number("TEST")
|
|
||||||
.device_class(0x02)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let irq = interrupt::take!(OTG_FS);
|
|
||||||
irq.set_priority(interrupt::Priority::P3);
|
|
||||||
|
|
||||||
let mut state = State::new();
|
|
||||||
let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
|
|
||||||
pin_mut!(usb);
|
|
||||||
|
|
||||||
let (mut reader, mut writer) = usb.as_ref().take_serial_0();
|
|
||||||
|
|
||||||
info!("usb initialized!");
|
|
||||||
|
|
||||||
unwrap!(
|
|
||||||
writer
|
|
||||||
.write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
|
|
||||||
.await
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut buf = [0u8; 64];
|
|
||||||
loop {
|
|
||||||
let mut n = 0;
|
|
||||||
|
|
||||||
async {
|
|
||||||
loop {
|
|
||||||
let char = unwrap!(reader.read_byte().await);
|
|
||||||
|
|
||||||
if char == b'\r' || char == b'\n' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[n] = char;
|
|
||||||
n += 1;
|
|
||||||
|
|
||||||
// stop if we're out of room
|
|
||||||
if n == buf.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if n > 0 {
|
|
||||||
for char in buf[..n].iter_mut() {
|
|
||||||
// upper case
|
|
||||||
if 0x61 <= *char && *char <= 0x7a {
|
|
||||||
*char &= !0x20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unwrap!(writer.write_all(&buf[..n]).await);
|
|
||||||
unwrap!(writer.write_all(b"\r\n").await);
|
|
||||||
unwrap!(writer.flush().await);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
#![feature(type_alias_impl_trait)]
|
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use panic_probe as _;
|
|
||||||
|
|
||||||
use defmt::{info, unwrap};
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use embassy::interrupt::InterruptExt;
|
|
||||||
use futures::pin_mut;
|
|
||||||
use panic_probe as _; // print out panic messages
|
|
||||||
|
|
||||||
use embassy::executor::Spawner;
|
|
||||||
use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
|
|
||||||
use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
|
|
||||||
use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals};
|
|
||||||
use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
|
|
||||||
|
|
||||||
static mut EP_MEMORY: [u32; 2048] = [0; 2048];
|
|
||||||
|
|
||||||
// USB requires at least 48 MHz clock
|
|
||||||
fn config() -> Config {
|
|
||||||
let mut config = Config::default();
|
|
||||||
config.rcc.sys_ck = Some(Hertz(48_000_000));
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy::main(config = "config()")]
|
|
||||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
|
||||||
let mut rx_buffer = [0u8; 64];
|
|
||||||
// we send back input + cr + lf
|
|
||||||
let mut tx_buffer = [0u8; 66];
|
|
||||||
|
|
||||||
// USB with external high-speed PHY
|
|
||||||
let peri = UsbOtg::new_hs_ulpi(
|
|
||||||
p.USB_OTG_HS,
|
|
||||||
p.PA5,
|
|
||||||
p.PC2,
|
|
||||||
p.PC3,
|
|
||||||
p.PC0,
|
|
||||||
p.PA3,
|
|
||||||
p.PB0,
|
|
||||||
p.PB1,
|
|
||||||
p.PB10,
|
|
||||||
p.PB11,
|
|
||||||
p.PB12,
|
|
||||||
p.PB13,
|
|
||||||
p.PB5,
|
|
||||||
);
|
|
||||||
let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY });
|
|
||||||
|
|
||||||
let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
|
|
||||||
|
|
||||||
let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
|
|
||||||
.manufacturer("Fake company")
|
|
||||||
.product("Serial port")
|
|
||||||
.serial_number("TEST")
|
|
||||||
.device_class(0x02)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let irq = interrupt::take!(OTG_FS);
|
|
||||||
irq.set_priority(interrupt::Priority::P3);
|
|
||||||
|
|
||||||
let mut state = State::new();
|
|
||||||
let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
|
|
||||||
pin_mut!(usb);
|
|
||||||
|
|
||||||
let (mut reader, mut writer) = usb.as_ref().take_serial_0();
|
|
||||||
|
|
||||||
info!("usb initialized!");
|
|
||||||
|
|
||||||
unwrap!(
|
|
||||||
writer
|
|
||||||
.write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
|
|
||||||
.await
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut buf = [0u8; 64];
|
|
||||||
loop {
|
|
||||||
let mut n = 0;
|
|
||||||
|
|
||||||
async {
|
|
||||||
loop {
|
|
||||||
let char = unwrap!(reader.read_byte().await);
|
|
||||||
|
|
||||||
if char == b'\r' || char == b'\n' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[n] = char;
|
|
||||||
n += 1;
|
|
||||||
|
|
||||||
// stop if we're out of room
|
|
||||||
if n == buf.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if n > 0 {
|
|
||||||
for char in buf[..n].iter_mut() {
|
|
||||||
// upper case
|
|
||||||
if 0x61 <= *char && *char <= 0x7a {
|
|
||||||
*char &= !0x20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unwrap!(writer.write_all(&buf[..n]).await);
|
|
||||||
unwrap!(writer.write_all(b"\r\n").await);
|
|
||||||
unwrap!(writer.flush().await);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,6 +9,7 @@ resolver = "2"
|
|||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] }
|
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] }
|
||||||
embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] }
|
embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] }
|
||||||
|
embedded-io = { version = "0.2.0", features = ["async"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
@ -22,6 +23,7 @@ heapless = { version = "0.7.5", default-features = false }
|
|||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
rand_core = "0.6.3"
|
rand_core = "0.6.3"
|
||||||
critical-section = "0.2.3"
|
critical-section = "0.2.3"
|
||||||
|
embedded-storage = "0.3.0"
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
[dependencies.smoltcp]
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
@ -5,12 +5,10 @@
|
|||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy::executor::{Executor, Spawner};
|
use embassy::executor::{Executor, Spawner};
|
||||||
use embassy::io::AsyncWriteExt;
|
|
||||||
use embassy::time::{Duration, Timer};
|
use embassy::time::{Duration, Timer};
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
use embassy_net::{
|
use embassy_net::tcp::TcpSocket;
|
||||||
Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator, TcpSocket,
|
use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator};
|
||||||
};
|
|
||||||
use embassy_stm32::eth::generic_smi::GenericSMI;
|
use embassy_stm32::eth::generic_smi::GenericSMI;
|
||||||
use embassy_stm32::eth::{Ethernet, State};
|
use embassy_stm32::eth::{Ethernet, State};
|
||||||
use embassy_stm32::interrupt;
|
use embassy_stm32::interrupt;
|
||||||
@ -19,6 +17,7 @@ use embassy_stm32::peripherals::RNG;
|
|||||||
use embassy_stm32::rng::Rng;
|
use embassy_stm32::rng::Rng;
|
||||||
use embassy_stm32::time::U32Ext;
|
use embassy_stm32::time::U32Ext;
|
||||||
use embassy_stm32::Config;
|
use embassy_stm32::Config;
|
||||||
|
use embedded_io::asynch::Write;
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
use defmt_rtt as _; // global logger
|
||||||
|
59
examples/stm32f7/src/bin/flash.rs
Normal file
59
examples/stm32f7/src/bin/flash.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::{info, unwrap};
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::time::{Duration, Timer};
|
||||||
|
use embassy_stm32::flash::Flash;
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
|
||||||
|
|
||||||
|
use defmt_rtt as _; // global logger
|
||||||
|
use panic_probe as _;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
info!("Hello Flash!");
|
||||||
|
|
||||||
|
const ADDR: u32 = 0x8_0000;
|
||||||
|
|
||||||
|
// wait a bit before accessing the flash
|
||||||
|
Timer::after(Duration::from_millis(300)).await;
|
||||||
|
|
||||||
|
let mut f = Flash::unlock(p.FLASH);
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
unwrap!(f.read(ADDR, &mut buf));
|
||||||
|
info!("Read: {=[u8]:x}", buf);
|
||||||
|
|
||||||
|
info!("Erasing...");
|
||||||
|
unwrap!(f.erase(ADDR, ADDR + 256 * 1024));
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
unwrap!(f.read(ADDR, &mut buf));
|
||||||
|
info!("Read after erase: {=[u8]:x}", buf);
|
||||||
|
|
||||||
|
info!("Writing...");
|
||||||
|
unwrap!(f.write(
|
||||||
|
ADDR,
|
||||||
|
&[
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||||
|
25, 26, 27, 28, 29, 30, 31, 32
|
||||||
|
]
|
||||||
|
));
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
unwrap!(f.read(ADDR, &mut buf));
|
||||||
|
info!("Read: {=[u8]:x}", buf);
|
||||||
|
assert_eq!(
|
||||||
|
&buf[..],
|
||||||
|
&[
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||||
|
25, 26, 27, 28, 29, 30, 31, 32
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
@ -5,12 +5,11 @@ name = "embassy-stm32h7-examples"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[features]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] }
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
|
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
|
||||||
embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] }
|
embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] }
|
||||||
|
embedded-io = { version = "0.2.0", features = ["async"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
@ -27,6 +26,7 @@ rand_core = "0.6.3"
|
|||||||
critical-section = "0.2.5"
|
critical-section = "0.2.5"
|
||||||
micromath = "2.0.0"
|
micromath = "2.0.0"
|
||||||
stm32-fmc = "0.2.4"
|
stm32-fmc = "0.2.4"
|
||||||
|
embedded-storage = "0.3.0"
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
[dependencies.smoltcp]
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
@ -8,12 +8,10 @@ use panic_probe as _;
|
|||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy::executor::{Executor, Spawner};
|
use embassy::executor::{Executor, Spawner};
|
||||||
use embassy::io::AsyncWriteExt;
|
|
||||||
use embassy::time::{Duration, Timer};
|
use embassy::time::{Duration, Timer};
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
use embassy_net::{
|
use embassy_net::tcp::TcpSocket;
|
||||||
Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator, TcpSocket,
|
use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator};
|
||||||
};
|
|
||||||
use embassy_stm32::eth::generic_smi::GenericSMI;
|
use embassy_stm32::eth::generic_smi::GenericSMI;
|
||||||
use embassy_stm32::eth::{Ethernet, State};
|
use embassy_stm32::eth::{Ethernet, State};
|
||||||
use embassy_stm32::interrupt;
|
use embassy_stm32::interrupt;
|
||||||
@ -22,6 +20,7 @@ use embassy_stm32::peripherals::RNG;
|
|||||||
use embassy_stm32::rng::Rng;
|
use embassy_stm32::rng::Rng;
|
||||||
use embassy_stm32::time::U32Ext;
|
use embassy_stm32::time::U32Ext;
|
||||||
use embassy_stm32::Config;
|
use embassy_stm32::Config;
|
||||||
|
use embedded_io::asynch::Write;
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
#[embassy::task]
|
#[embassy::task]
|
||||||
|
58
examples/stm32h7/src/bin/flash.rs
Normal file
58
examples/stm32h7/src/bin/flash.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::{info, unwrap};
|
||||||
|
use defmt_rtt as _; // global logger
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::time::{Duration, Timer};
|
||||||
|
use embassy_stm32::flash::Flash;
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
|
||||||
|
use panic_probe as _;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
info!("Hello Flash!");
|
||||||
|
|
||||||
|
const ADDR: u32 = 0x08_0000;
|
||||||
|
|
||||||
|
// wait a bit before accessing the flash
|
||||||
|
Timer::after(Duration::from_millis(300)).await;
|
||||||
|
|
||||||
|
let mut f = Flash::unlock(p.FLASH);
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
unwrap!(f.read(ADDR, &mut buf));
|
||||||
|
info!("Read: {=[u8]:x}", buf);
|
||||||
|
|
||||||
|
info!("Erasing...");
|
||||||
|
unwrap!(f.erase(ADDR, ADDR + 128 * 1024));
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
unwrap!(f.read(ADDR, &mut buf));
|
||||||
|
info!("Read after erase: {=[u8]:x}", buf);
|
||||||
|
|
||||||
|
info!("Writing...");
|
||||||
|
unwrap!(f.write(
|
||||||
|
ADDR,
|
||||||
|
&[
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||||
|
25, 26, 27, 28, 29, 30, 31, 32
|
||||||
|
]
|
||||||
|
));
|
||||||
|
|
||||||
|
info!("Reading...");
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
unwrap!(f.read(ADDR, &mut buf));
|
||||||
|
info!("Read: {=[u8]:x}", buf);
|
||||||
|
assert_eq!(
|
||||||
|
&buf[..],
|
||||||
|
&[
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||||
|
25, 26, 27, 28, 29, 30, 31, 32
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# replace your chip as listed in `probe-run --list-chips`
|
# replace your chip as listed in `probe-run --list-chips`
|
||||||
runner = "probe-run --chip STM32L072CZTx"
|
runner = "probe-run --chip STM32L053R8Tx"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv6m-none-eabi"
|
target = "thumbv6m-none-eabi"
|
||||||
|
@ -7,12 +7,11 @@ resolver = "2"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["nightly"]
|
default = ["nightly"]
|
||||||
nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan"]
|
nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", "embedded-io/async"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
|
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
|
||||||
|
|
||||||
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true}
|
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true}
|
||||||
|
|
||||||
lorawan-device = { version = "0.7.1", default-features = false, features = ["async"], optional = true }
|
lorawan-device = { version = "0.7.1", default-features = false, features = ["async"], optional = true }
|
||||||
@ -22,6 +21,7 @@ defmt = "0.3"
|
|||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
|
|
||||||
embedded-storage = "0.3.0"
|
embedded-storage = "0.3.0"
|
||||||
|
embedded-io = "0.2.0"
|
||||||
|
|
||||||
cortex-m = "0.7.3"
|
cortex-m = "0.7.3"
|
||||||
cortex-m-rt = "0.7.0"
|
cortex-m-rt = "0.7.0"
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
use defmt_rtt as _;
|
||||||
|
use embedded_io::asynch::{Read, Write};
|
||||||
|
// global logger
|
||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
|
|
||||||
use embassy::executor::Spawner;
|
use embassy::executor::Spawner;
|
||||||
use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
|
|
||||||
use embassy_stm32::dma::NoDma;
|
use embassy_stm32::dma::NoDma;
|
||||||
use embassy_stm32::interrupt;
|
use embassy_stm32::interrupt;
|
||||||
use embassy_stm32::usart::{BufferedUart, Config, State, Uart};
|
use embassy_stm32::usart::{BufferedUart, Config, State, Uart};
|
||||||
@ -16,19 +17,21 @@ use embassy_stm32::Peripherals;
|
|||||||
|
|
||||||
#[embassy::main]
|
#[embassy::main]
|
||||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
info!("Hi!");
|
||||||
|
|
||||||
static mut TX_BUFFER: [u8; 8] = [0; 8];
|
static mut TX_BUFFER: [u8; 8] = [0; 8];
|
||||||
static mut RX_BUFFER: [u8; 256] = [0; 256];
|
static mut RX_BUFFER: [u8; 256] = [0; 256];
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.baudrate = 9600;
|
config.baudrate = 9600;
|
||||||
|
|
||||||
let usart = Uart::new(p.USART1, p.PA10, p.PA9, NoDma, NoDma, config);
|
let usart = Uart::new(p.USART2, p.PA3, p.PA2, NoDma, NoDma, config);
|
||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
let mut usart = unsafe {
|
let mut usart = unsafe {
|
||||||
BufferedUart::new(
|
BufferedUart::new(
|
||||||
&mut state,
|
&mut state,
|
||||||
usart,
|
usart,
|
||||||
interrupt::take!(USART1),
|
interrupt::take!(USART2),
|
||||||
&mut TX_BUFFER,
|
&mut TX_BUFFER,
|
||||||
&mut RX_BUFFER,
|
&mut RX_BUFFER,
|
||||||
)
|
)
|
||||||
|
@ -10,7 +10,7 @@ resolver = "2"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||||
embassy-traits = { version = "0.1.0", path = "../../embassy-traits" }
|
embassy-traits = { version = "0.1.0", path = "../../embassy-traits" }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "usb-otg"] }
|
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
#![feature(type_alias_impl_trait)]
|
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use panic_probe as _;
|
|
||||||
|
|
||||||
use defmt::{info, unwrap};
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use embassy::interrupt::InterruptExt;
|
|
||||||
use futures::pin_mut;
|
|
||||||
use panic_probe as _; // print out panic messages
|
|
||||||
|
|
||||||
use embassy::executor::Spawner;
|
|
||||||
use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
|
|
||||||
use embassy_stm32::pac::pwr::vals::Usv;
|
|
||||||
use embassy_stm32::pac::{PWR, RCC};
|
|
||||||
use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
|
|
||||||
use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
|
|
||||||
use embassy_stm32::{interrupt, Config, Peripherals};
|
|
||||||
use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
|
|
||||||
|
|
||||||
static mut EP_MEMORY: [u32; 2048] = [0; 2048];
|
|
||||||
|
|
||||||
// USB requires at least 48 MHz clock
|
|
||||||
fn config() -> Config {
|
|
||||||
let mut config = Config::default();
|
|
||||||
// set up a 80Mhz clock
|
|
||||||
config.rcc.mux = ClockSrc::PLL(
|
|
||||||
PLLSource::HSI16,
|
|
||||||
PLLClkDiv::Div2,
|
|
||||||
PLLSrcDiv::Div2,
|
|
||||||
PLLMul::Mul20,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
// enable HSI48 clock for USB
|
|
||||||
config.rcc.hsi48 = true;
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy::main(config = "config()")]
|
|
||||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
|
||||||
// Enable PWR peripheral
|
|
||||||
unsafe { RCC.apb1enr1().modify(|w| w.set_pwren(true)) };
|
|
||||||
unsafe { PWR.cr2().modify(|w| w.set_usv(Usv::VALID)) }
|
|
||||||
|
|
||||||
let mut rx_buffer = [0u8; 64];
|
|
||||||
// we send back input + cr + lf
|
|
||||||
let mut tx_buffer = [0u8; 66];
|
|
||||||
|
|
||||||
let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11);
|
|
||||||
let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY });
|
|
||||||
|
|
||||||
let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
|
|
||||||
|
|
||||||
let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
|
|
||||||
.manufacturer("Fake company")
|
|
||||||
.product("Serial port")
|
|
||||||
.serial_number("TEST")
|
|
||||||
.device_class(0x02)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let irq = interrupt::take!(OTG_FS);
|
|
||||||
irq.set_priority(interrupt::Priority::P3);
|
|
||||||
|
|
||||||
let mut state = State::new();
|
|
||||||
let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
|
|
||||||
pin_mut!(usb);
|
|
||||||
|
|
||||||
let (mut reader, mut writer) = usb.as_ref().take_serial_0();
|
|
||||||
|
|
||||||
info!("usb initialized!");
|
|
||||||
|
|
||||||
unwrap!(
|
|
||||||
writer
|
|
||||||
.write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
|
|
||||||
.await
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut buf = [0u8; 64];
|
|
||||||
loop {
|
|
||||||
let mut n = 0;
|
|
||||||
|
|
||||||
async {
|
|
||||||
loop {
|
|
||||||
let char = unwrap!(reader.read_byte().await);
|
|
||||||
|
|
||||||
if char == b'\r' || char == b'\n' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[n] = char;
|
|
||||||
n += 1;
|
|
||||||
|
|
||||||
// stop if we're out of room
|
|
||||||
if n == buf.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if n > 0 {
|
|
||||||
for char in buf[..n].iter_mut() {
|
|
||||||
// upper case
|
|
||||||
if 0x61 <= *char && *char <= 0x7a {
|
|
||||||
*char &= !0x20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unwrap!(writer.write_all(&buf[..n]).await);
|
|
||||||
unwrap!(writer.write_all(b"\r\n").await);
|
|
||||||
unwrap!(writer.flush().await);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +1 @@
|
|||||||
Subproject commit 9abfa9d2b51e6071fdc7e680b4a171e4fa20c2fb
|
Subproject commit b2d7a9f5de7dc3ae17c87c1ff94e13a822d18e74
|
Loading…
Reference in New Issue
Block a user