vorago_shared_hal/uart/
tx_async.rs1use core::{cell::RefCell, future::Future};
11
12use critical_section::Mutex;
13use embassy_sync::waitqueue::AtomicWaker;
14use embedded_io_async::Write;
15use portable_atomic::AtomicBool;
16use raw_slice::RawBufSlice;
17
18use super::*;
19
20static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
21static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
22 [const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
23static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
26
27#[inline]
28fn tx_is_drained(tx: &Tx) -> bool {
29 let tx_status = tx.regs.read_tx_status();
30 tx.regs.read_state().tx_fifo().value() == 0 && !tx_status.tx_busy()
31}
32
33pub fn on_interrupt_tx(bank: Bank) {
39 let mut uart = unsafe { bank.steal_regs() };
40 let idx = bank as usize;
41 let irq_enabled = uart.read_interrupt_enable();
42 if !irq_enabled.tx_below_trigger() && !irq_enabled.tx_empty() {
44 return;
45 }
46
47 let tx_status = uart.read_tx_status();
48 let interrupt_status = uart.read_interrupt_status();
49 let unexpected_overrun = tx_status.wr_lost();
50 let mut context = critical_section::with(|cs| {
51 let context_ref = TX_CONTEXTS[idx].borrow(cs);
52 *context_ref.borrow()
53 });
54 context.tx_overrun = unexpected_overrun;
55 let slice = unsafe { context.slice.get().unwrap() };
58 if context.progress >= slice.len() && interrupt_status.tx_empty() {
59 uart.modify_interrupt_enable(|value| {
60 value
61 .with_tx_below_trigger(false)
62 .with_tx_empty(false)
63 .with_tx_status(false)
64 });
65 uart.modify_enable(|value| value.with_tx(false));
66 critical_section::with(|cs| {
68 let context_ref = TX_CONTEXTS[idx].borrow(cs);
69 *context_ref.borrow_mut() = context;
70 });
71 TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
73 UART_TX_WAKERS[idx].wake();
74 return;
75 }
76 while context.progress < slice.len() {
77 if !uart.read_tx_status().ready() {
78 break;
79 }
80 uart.write_data(Data::new_with_raw_value(slice[context.progress] as u32));
83 context.progress += 1;
84 }
85 if context.progress == slice.len() {
87 uart.modify_interrupt_enable(|value| value.with_tx_below_trigger(false));
88 }
89
90 critical_section::with(|cs| {
92 let context_ref = TX_CONTEXTS[idx].borrow(cs);
93 *context_ref.borrow_mut() = context;
94 });
95}
96
97#[derive(Debug, Copy, Clone)]
98pub struct TxContext {
99 progress: usize,
100 tx_overrun: bool,
101 slice: RawBufSlice,
102}
103
104#[allow(clippy::new_without_default)]
105impl TxContext {
106 pub const fn new() -> Self {
107 Self {
108 progress: 0,
109 tx_overrun: false,
110 slice: RawBufSlice::new_nulled(),
111 }
112 }
113}
114
115pub struct TxFuture {
116 id: Bank,
117}
118
119impl TxFuture {
120 pub unsafe fn new(tx: &mut Tx, data: &[u8]) -> Self {
125 TX_DONE[tx.id as usize].store(false, core::sync::atomic::Ordering::Relaxed);
126 tx.disable_interrupts();
127 tx.disable();
128 tx.clear_fifo();
129
130 let init_fill_count = core::cmp::min(data.len(), FIFO_DEPTH);
131 for data in data.iter().take(init_fill_count) {
133 tx.regs.write_data(Data::new_with_raw_value(*data as u32));
134 }
135 critical_section::with(|cs| {
136 let context_ref = TX_CONTEXTS[tx.id as usize].borrow(cs);
137 let mut context = context_ref.borrow_mut();
138 unsafe { context.slice.set(data) };
139 context.progress = init_fill_count;
140
141 tx.enable_interrupts(
144 data.len() > FIFO_DEPTH,
145 #[cfg(feature = "vor4x")]
146 true,
147 );
148 tx.enable();
149 });
150 Self { id: tx.id }
151 }
152}
153
154impl Future for TxFuture {
155 type Output = Result<usize, TxOverrunError>;
156
157 fn poll(
158 self: core::pin::Pin<&mut Self>,
159 cx: &mut core::task::Context<'_>,
160 ) -> core::task::Poll<Self::Output> {
161 UART_TX_WAKERS[self.id as usize].register(cx.waker());
162 if TX_DONE[self.id as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
163 let progress = critical_section::with(|cs| {
164 TX_CONTEXTS[self.id as usize].borrow(cs).borrow().progress
165 });
166 return core::task::Poll::Ready(Ok(progress));
167 }
168 core::task::Poll::Pending
169 }
170}
171
172impl Drop for TxFuture {
173 fn drop(&mut self) {
174 let mut reg_block = unsafe { self.id.steal_regs() };
175 if !TX_DONE[self.id as usize].load(core::sync::atomic::Ordering::Relaxed) {
176 disable_tx_interrupts(&mut reg_block);
177 disable_tx(&mut reg_block);
178 }
179 }
180}
181
182pub struct TxAsync(Tx);
183
184impl TxAsync {
185 pub fn new(tx: Tx) -> Self {
186 Self(tx)
187 }
188
189 #[inline]
190 pub fn inner(&mut self) -> &mut Tx {
191 &mut self.0
192 }
193
194 pub async fn write(&mut self, buf: &[u8]) -> Result<usize, TxOverrunError> {
199 let fut = unsafe { TxFuture::new(&mut self.0, buf) };
200 fut.await
201 }
202
203 pub async fn write_all(&mut self, buf: &[u8]) -> Result<(), TxOverrunError> {
211 let fut = <Self as embedded_io_async::Write>::write_all(self, buf);
212 fut.await
213 }
214
215 pub async fn flush(&mut self) -> Result<(), TxOverrunError> {
216 while !tx_is_drained(&self.0) {}
217 Ok(())
218 }
219
220 pub fn release(self) -> Tx {
221 self.0
222 }
223}
224
225#[derive(Debug, thiserror::Error)]
226#[cfg_attr(feature = "defmt", derive(defmt::Format))]
227#[error("TX overrun error")]
228pub struct TxOverrunError;
229
230impl embedded_io_async::Error for TxOverrunError {
231 fn kind(&self) -> embedded_io_async::ErrorKind {
232 embedded_io_async::ErrorKind::Other
233 }
234}
235
236impl embedded_io::ErrorType for TxAsync {
237 type Error = TxOverrunError;
238}
239
240impl Write for TxAsync {
241 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
246 self.write(buf).await
247 }
248
249 async fn flush(&mut self) -> Result<(), Self::Error> {
250 self.flush().await
251 }
252}