use core::{cell::RefCell, future::Future};
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_io_async::Write;
use portable_atomic::AtomicBool;
use raw_slice::RawBufSlice;
use super::*;
static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
[const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
#[inline]
fn tx_is_drained(tx: &Tx) -> bool {
let tx_status = tx.regs.read_tx_status();
tx.regs.read_state().tx_fifo().value() == 0 && !tx_status.tx_busy()
}
pub fn on_interrupt_tx(bank: Bank) {
let mut uart = unsafe { bank.steal_regs() };
let idx = bank as usize;
let irq_enabled = uart.read_interrupt_enable();
if !irq_enabled.tx_below_trigger() && !irq_enabled.tx_empty() {
return;
}
let tx_status = uart.read_tx_status();
let interrupt_status = uart.read_interrupt_status();
let unexpected_overrun = tx_status.wr_lost();
let mut context = critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow()
});
context.tx_overrun = unexpected_overrun;
let slice = unsafe { context.slice.get().unwrap() };
if context.progress >= slice.len() && interrupt_status.tx_empty() {
uart.modify_interrupt_enable(|value| {
value
.with_tx_below_trigger(false)
.with_tx_empty(false)
.with_tx_status(false)
});
uart.modify_enable(|value| value.with_tx(false));
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = context;
});
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
UART_TX_WAKERS[idx].wake();
return;
}
while context.progress < slice.len() {
if !uart.read_tx_status().ready() {
break;
}
uart.write_data(Data::new_with_raw_value(slice[context.progress] as u32));
context.progress += 1;
}
if context.progress == slice.len() {
uart.modify_interrupt_enable(|value| value.with_tx_below_trigger(false));
}
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = context;
});
}
#[derive(Debug, Copy, Clone)]
pub struct TxContext {
progress: usize,
tx_overrun: bool,
slice: RawBufSlice,
}
#[allow(clippy::new_without_default)]
impl TxContext {
pub const fn new() -> Self {
Self {
progress: 0,
tx_overrun: false,
slice: RawBufSlice::new_nulled(),
}
}
}
pub struct TxFuture {
id: Bank,
}
impl TxFuture {
pub unsafe fn new(tx: &mut Tx, data: &[u8]) -> Self {
TX_DONE[tx.id as usize].store(false, core::sync::atomic::Ordering::Relaxed);
tx.disable_interrupts();
tx.disable();
tx.clear_fifo();
let init_fill_count = core::cmp::min(data.len(), FIFO_DEPTH);
for data in data.iter().take(init_fill_count) {
tx.regs.write_data(Data::new_with_raw_value(*data as u32));
}
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[tx.id as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
unsafe { context.slice.set(data) };
context.progress = init_fill_count;
tx.enable_interrupts(
data.len() > FIFO_DEPTH,
#[cfg(feature = "vor4x")]
true,
);
tx.enable();
});
Self { id: tx.id }
}
}
impl Future for TxFuture {
type Output = Result<usize, TxOverrunError>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
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| {
TX_CONTEXTS[self.id as usize].borrow(cs).borrow().progress
});
return core::task::Poll::Ready(Ok(progress));
}
core::task::Poll::Pending
}
}
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) {
disable_tx_interrupts(&mut reg_block);
disable_tx(&mut reg_block);
}
}
}
pub struct TxAsync(Tx);
impl TxAsync {
pub fn new(tx: Tx) -> Self {
Self(tx)
}
#[inline]
pub fn inner(&mut self) -> &mut Tx {
&mut self.0
}
pub async fn write(&mut self, buf: &[u8]) -> Result<usize, TxOverrunError> {
let fut = unsafe { TxFuture::new(&mut self.0, buf) };
fut.await
}
pub async fn write_all(&mut self, buf: &[u8]) -> Result<(), TxOverrunError> {
let fut = <Self as embedded_io_async::Write>::write_all(self, buf);
fut.await
}
pub async fn flush(&mut self) -> Result<(), TxOverrunError> {
while !tx_is_drained(&self.0) {}
Ok(())
}
pub fn release(self) -> Tx {
self.0
}
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("TX overrun error")]
pub struct TxOverrunError;
impl embedded_io_async::Error for TxOverrunError {
fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other
}
}
impl embedded_io::ErrorType for TxAsync {
type Error = TxOverrunError;
}
impl Write for TxAsync {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.write(buf).await
}
async fn flush(&mut self) -> Result<(), Self::Error> {
self.flush().await
}
}