TX async writes are unsafe now
shared-hal-ci / Check build (push) Has been cancelled
shared-hal-ci / Check formatting (push) Has been cancelled
shared-hal-ci / Check Documentation Build (push) Has been cancelled
shared-hal-ci / Clippy (push) Has been cancelled
shared-hal-ci / Check build (pull_request) Has been cancelled
shared-hal-ci / Check formatting (pull_request) Has been cancelled
shared-hal-ci / Check Documentation Build (pull_request) Has been cancelled
shared-hal-ci / Clippy (pull_request) Has been cancelled
va108xx-ci / Run Tests (push) Has been cancelled
va108xx-ci / Check formatting (push) Has been cancelled
va108xx-ci / Check Documentation Build (push) Has been cancelled
va108xx-ci / Clippy (push) Has been cancelled
va108xx-ci / Run Tests (pull_request) Has been cancelled
va108xx-ci / Check formatting (pull_request) Has been cancelled
va108xx-ci / Check Documentation Build (pull_request) Has been cancelled
va108xx-ci / Clippy (pull_request) Has been cancelled
va416xx-ci / Run Tests (push) Has been cancelled
va416xx-ci / Check formatting (push) Has been cancelled
va416xx-ci / Check Documentation Build (push) Has been cancelled
va416xx-ci / Clippy (push) Has been cancelled
va416xx-ci / Run Tests (pull_request) Has been cancelled
va416xx-ci / Check formatting (pull_request) Has been cancelled
va416xx-ci / Check Documentation Build (pull_request) Has been cancelled
va416xx-ci / Clippy (pull_request) Has been cancelled
va108xx-ci / Check build (push) Has been cancelled
va108xx-ci / Check build (pull_request) Has been cancelled
va416xx-ci / Check build (push) Has been cancelled
va416xx-ci / Check build (pull_request) Has been cancelled
shared-hal-ci / Check build (push) Has been cancelled
shared-hal-ci / Check formatting (push) Has been cancelled
shared-hal-ci / Check Documentation Build (push) Has been cancelled
shared-hal-ci / Clippy (push) Has been cancelled
shared-hal-ci / Check build (pull_request) Has been cancelled
shared-hal-ci / Check formatting (pull_request) Has been cancelled
shared-hal-ci / Check Documentation Build (pull_request) Has been cancelled
shared-hal-ci / Clippy (pull_request) Has been cancelled
va108xx-ci / Run Tests (push) Has been cancelled
va108xx-ci / Check formatting (push) Has been cancelled
va108xx-ci / Check Documentation Build (push) Has been cancelled
va108xx-ci / Clippy (push) Has been cancelled
va108xx-ci / Run Tests (pull_request) Has been cancelled
va108xx-ci / Check formatting (pull_request) Has been cancelled
va108xx-ci / Check Documentation Build (pull_request) Has been cancelled
va108xx-ci / Clippy (pull_request) Has been cancelled
va416xx-ci / Run Tests (push) Has been cancelled
va416xx-ci / Check formatting (push) Has been cancelled
va416xx-ci / Check Documentation Build (push) Has been cancelled
va416xx-ci / Clippy (push) Has been cancelled
va416xx-ci / Run Tests (pull_request) Has been cancelled
va416xx-ci / Check formatting (pull_request) Has been cancelled
va416xx-ci / Check Documentation Build (pull_request) Has been cancelled
va416xx-ci / Clippy (pull_request) Has been cancelled
va108xx-ci / Check build (push) Has been cancelled
va108xx-ci / Check build (pull_request) Has been cancelled
va416xx-ci / Check build (push) Has been cancelled
va416xx-ci / Check build (pull_request) Has been cancelled
This commit is contained in:
@@ -70,10 +70,13 @@ async fn main(_spawner: Spawner) {
|
||||
led0.toggle();
|
||||
led1.toggle();
|
||||
led2.toggle();
|
||||
async_tx
|
||||
.write_all(STR_LIST[idx].as_bytes())
|
||||
.await
|
||||
.expect("writing failed");
|
||||
// Safety: We are sending static lifetime slices, and not cancelling the futures.
|
||||
unsafe {
|
||||
async_tx
|
||||
.write_all(STR_LIST[idx].as_bytes())
|
||||
.await
|
||||
.expect("writing failed");
|
||||
}
|
||||
idx += 1;
|
||||
if idx == STR_LIST.len() {
|
||||
idx = 0;
|
||||
|
||||
@@ -284,7 +284,8 @@ mod app {
|
||||
let mut buf: [u8; 256] = [0; 256];
|
||||
loop {
|
||||
let read_len = cx.local.tm_rx.read(&mut buf).await;
|
||||
if let Err(e) = cx.local.uart_tx.write_all(&buf[0..read_len]).await {
|
||||
// Safety: The buffer outlives the UART TX structure.
|
||||
if let Err(e) = unsafe { cx.local.uart_tx.write_all(&buf[0..read_len]).await } {
|
||||
defmt::warn!("UART TX overrun error: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- Async TX UART functions are explicitely marked `unsafe`.
|
||||
- Async TX UART `write` now returns a `TxFuture`
|
||||
- Empty async TX write resolves to `Poll::Ready(0)` immediately
|
||||
|
||||
## [v0.13.1] 2026-05-19
|
||||
|
||||
- Docs.rs feature fix
|
||||
|
||||
@@ -74,10 +74,13 @@ async fn main(_spawner: Spawner) {
|
||||
loop {
|
||||
defmt::println!("Current time: {}", Instant::now().as_secs());
|
||||
led.toggle();
|
||||
async_tx
|
||||
.write_all(STR_LIST[idx].as_bytes())
|
||||
.await
|
||||
.expect("writing failed");
|
||||
// Safety: We are sending static lifetime slices, and not cancelling the futures.
|
||||
unsafe {
|
||||
async_tx
|
||||
.write_all(STR_LIST[idx].as_bytes())
|
||||
.await
|
||||
.expect("writing failed");
|
||||
}
|
||||
idx += 1;
|
||||
if idx == STR_LIST.len() {
|
||||
idx = 0;
|
||||
|
||||
@@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- Async TX UART functions are explicitely marked `unsafe`.
|
||||
- Async TX UART `write` now returns a `TxFuture`
|
||||
- Empty async TX write resolves to `Poll::Ready(0)` immediately
|
||||
|
||||
# [v0.6.0] 2025-09-03
|
||||
|
||||
- Use `vorago-shared-hal` dependency to provide shared peripherals.
|
||||
|
||||
@@ -8,9 +8,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- Async TX UART functions are explicitely marked `unsafe`.
|
||||
- Async TX UART `write` now returns a `TxFuture`
|
||||
- Empty async TX write resolves to `Poll::Ready(0)` immediately
|
||||
|
||||
## [v0.4.0] 2026-05-19
|
||||
|
||||
## Changed
|
||||
### Changed
|
||||
|
||||
- Naming improvements for UART register module
|
||||
- Improved UART Async TX module. Only enable TX below threshold interrupts if the FIFO
|
||||
|
||||
@@ -1138,6 +1138,14 @@ pub struct Tx {
|
||||
regs: regs::MmioUart<'static>,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Tx {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("Tx")
|
||||
.field("id", &self.id)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl Tx {
|
||||
/// Retrieve a TX pin without expecting an explicit UART structure
|
||||
///
|
||||
|
||||
@@ -112,8 +112,10 @@ impl TxContext {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TxFuture {
|
||||
id: Bank,
|
||||
empty_buffer: bool,
|
||||
}
|
||||
|
||||
impl TxFuture {
|
||||
@@ -122,6 +124,14 @@ impl TxFuture {
|
||||
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
||||
/// that the slice outlives the data structure.
|
||||
pub unsafe fn new(tx: &mut Tx, data: &[u8]) -> Self {
|
||||
if data.is_empty() {
|
||||
// We can just return a dummy future which is immediately ready, no need to set up
|
||||
// interrupts etc.
|
||||
return Self {
|
||||
id: tx.id,
|
||||
empty_buffer: true,
|
||||
};
|
||||
}
|
||||
TX_DONE[tx.id as usize].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
tx.disable_interrupts();
|
||||
tx.disable();
|
||||
@@ -147,7 +157,10 @@ impl TxFuture {
|
||||
);
|
||||
tx.enable();
|
||||
});
|
||||
Self { id: tx.id }
|
||||
Self {
|
||||
id: tx.id,
|
||||
empty_buffer: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +171,9 @@ impl Future for TxFuture {
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> core::task::Poll<Self::Output> {
|
||||
if self.empty_buffer {
|
||||
return core::task::Poll::Ready(Ok(0));
|
||||
}
|
||||
UART_TX_WAKERS[self.id as usize].register(cx.waker());
|
||||
if TX_DONE[self.id as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||
let progress = critical_section::with(|cs| {
|
||||
@@ -169,16 +185,23 @@ impl Future for TxFuture {
|
||||
}
|
||||
}
|
||||
|
||||
/// Safety note:
|
||||
///
|
||||
/// It is imperative that this `Drop` method is executed to avoid undefined behaviour on
|
||||
/// transfer. Do *NOT* use `core::mem::forget` on the `TxFuture`.
|
||||
impl Drop for TxFuture {
|
||||
fn drop(&mut self) {
|
||||
let mut reg_block = unsafe { self.id.steal_regs() };
|
||||
if !TX_DONE[self.id as usize].load(core::sync::atomic::Ordering::Relaxed) {
|
||||
if !TX_DONE[self.id as usize].load(core::sync::atomic::Ordering::Relaxed)
|
||||
&& !self.empty_buffer
|
||||
{
|
||||
disable_tx_interrupts(&mut reg_block);
|
||||
disable_tx(&mut reg_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TxAsync(Tx);
|
||||
|
||||
impl TxAsync {
|
||||
@@ -195,9 +218,13 @@ impl TxAsync {
|
||||
///
|
||||
/// This implementation is not side effect free, and a started future might have already
|
||||
/// written part of the passed buffer.
|
||||
pub async fn write(&mut self, buf: &[u8]) -> Result<usize, TxOverrunError> {
|
||||
let fut = unsafe { TxFuture::new(&mut self.0, buf) };
|
||||
fut.await
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
||||
/// that the slice outlives the data structure.
|
||||
pub unsafe fn write(&mut self, buf: &[u8]) -> TxFuture {
|
||||
unsafe { TxFuture::new(&mut self.0, buf) }
|
||||
}
|
||||
|
||||
/// Write an entire buffer into this writer.
|
||||
@@ -207,7 +234,12 @@ impl TxAsync {
|
||||
///
|
||||
/// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned
|
||||
/// future that hasn't completed yet, some bytes might have already been written.
|
||||
pub async fn write_all(&mut self, buf: &[u8]) -> Result<(), TxOverrunError> {
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
||||
/// that the slice outlives the data structure.
|
||||
pub async unsafe fn write_all(&mut self, buf: &[u8]) -> Result<(), TxOverrunError> {
|
||||
let fut = <Self as embedded_io_async::Write>::write_all(self, buf);
|
||||
fut.await
|
||||
}
|
||||
@@ -242,8 +274,16 @@ impl Write for TxAsync {
|
||||
///
|
||||
/// This implementation is not side effect free, and a started future might have already
|
||||
/// written part of the passed buffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is not `unsafe` due to the trait definition.
|
||||
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
||||
/// that the slice outlives the data structure.
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.write(buf).await
|
||||
// Safety: We documented the safety contract. Not much else we can do here as we are bound
|
||||
// by the trait definition.
|
||||
unsafe { self.write(buf).await }
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
|
||||
Reference in New Issue
Block a user