use core::future::poll_fn;
use core::marker::PhantomData;
use core::mem;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_internal::PeripheralRef;
use embedded_io_async::ReadReady;
use futures_util::future::{select, Either};
use super::{
clear_interrupt_flags, reconfigure, set_baudrate, Config, ConfigError, Error, Instance, UartRx,
};
use crate::dma::ReadableRingBuffer;
use crate::gpio::{AnyPin, SealedPin as _};
use crate::mode::Async;
use crate::pac::usart::Usart as Regs;
pub struct RingBufferedUartRx<'d, T: Instance> {
rx: Option<PeripheralRef<'d, AnyPin>>,
rts: Option<PeripheralRef<'d, AnyPin>>,
ring_buf: ReadableRingBuffer<'d, u8>,
_phantom: PhantomData<T>,
}
impl<'d, T: Instance> SetConfig for RingBufferedUartRx<'d, T> {
type Config = Config;
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.set_config(config)
}
}
impl<'d, T: Instance> UartRx<'d, T, Async> {
pub fn into_ring_buffered(mut self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d, T> {
assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
let opts = Default::default();
let dma_ch = self.rx_dma.take().unwrap();
let ring_buf = unsafe {
ReadableRingBuffer::new(
dma_ch.channel,
dma_ch.request,
T::regs().rdr().as_ptr() as _,
dma_buf,
opts,
)
};
let rx = self.rx.take();
let rts = self.rts.take();
mem::forget(self);
RingBufferedUartRx {
rx,
rts,
ring_buf,
_phantom: PhantomData,
}
}
}
impl<'d, T: Instance> RingBufferedUartRx<'d, T> {
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
reconfigure::<T>(T::frequency().unwrap(), config)
}
pub fn start_uart(&mut self) {
compiler_fence(Ordering::SeqCst);
self.ring_buf.start();
let r = T::regs();
r.cr1().modify(|w| {
w.set_rxneie(false);
w.set_peie(w.pce());
w.set_idleie(true);
});
r.cr3().modify(|w| {
w.set_eie(true);
w.set_dmar(true);
});
}
fn stop_uart(&mut self) {
self.ring_buf.request_pause();
let r = T::regs();
r.cr1().modify(|w| {
w.set_rxneie(false);
w.set_peie(false);
w.set_idleie(false);
});
r.cr3().modify(|w| {
w.set_eie(false);
w.set_dmar(false);
});
compiler_fence(Ordering::SeqCst);
}
fn start_dma_or_check_errors(&mut self) -> Result<(), Error> {
let r = T::regs();
check_idle_and_errors(r)?;
if !r.cr3().read().dmar() {
self.start_uart();
}
Ok(())
}
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
self.start_dma_or_check_errors()?;
let r = T::regs();
if r.cr3().read().hdsel() && r.cr1().read().te() {
r.cr1().modify(|reg| {
reg.set_re(true);
reg.set_te(false);
});
}
loop {
match self.ring_buf.read(buf) {
Ok((0, _)) => {}
Ok((len, _)) => {
return Ok(len);
}
Err(_) => {
self.stop_uart();
return Err(Error::Overrun);
}
}
match self.wait_for_data_or_idle().await {
Ok(_) => {}
Err(err) => {
self.stop_uart();
return Err(err);
}
}
}
}
async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> {
compiler_fence(Ordering::SeqCst);
let state = T::state();
let uart_idle = poll_fn(|cx| {
state.rx_waker.register(cx.waker());
compiler_fence(Ordering::SeqCst);
if check_idle_and_errors(T::regs())? {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
});
let mut dma_init = false;
let dma = poll_fn(|cx| {
self.ring_buf.set_waker(cx.waker());
let status = match dma_init {
false => Poll::Pending,
true => Poll::Ready(()),
};
dma_init = true;
status
});
match select(uart_idle, dma).await {
Either::Left((result, _)) => result,
Either::Right(((), _)) => Ok(()),
}
}
pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {
set_baudrate::<T>(T::frequency().unwrap(), baudrate)
}
}
impl<T: Instance> Drop for RingBufferedUartRx<'_, T> {
fn drop(&mut self) {
self.stop_uart();
self.rx.as_ref().map(|x| x.set_as_disconnected());
self.rts.as_ref().map(|x| x.set_as_disconnected());
super::drop_tx_rx::<T>(T::state());
}
}
fn check_idle_and_errors(r: Regs) -> Result<bool, Error> {
let sr = critical_section::with(|_| {
let sr = r.isr().read();
if sr.rxne() || sr.ore() || sr.idle() {
let _ = unsafe {
r.rdr().as_ptr().read_volatile().0;
};
}
clear_interrupt_flags(r, sr);
sr
});
if sr.pe() {
Err(Error::Parity)
} else if sr.fe() {
Err(Error::Framing)
} else if sr.nf() {
Err(Error::Noise)
} else if sr.ore() {
Err(Error::Overrun)
} else {
r.cr1().modify(|w| w.set_idleie(true));
Ok(sr.idle())
}
}
impl<T: Instance> embedded_io_async::ErrorType for RingBufferedUartRx<'_, T> {
type Error = Error;
}
impl<T: Instance> embedded_io_async::Read for RingBufferedUartRx<'_, T> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.read(buf).await
}
}
impl<T: Instance> embedded_hal_nb::serial::Read for RingBufferedUartRx<'_, T> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.start_dma_or_check_errors()?;
let mut buf = [0u8; 1];
match self.ring_buf.read(&mut buf) {
Ok((0, _)) => Err(nb::Error::WouldBlock),
Ok((len, _)) => {
assert!(len == 1);
Ok(buf[0])
}
Err(_) => {
self.stop_uart();
Err(nb::Error::Other(Error::Overrun))
}
}
}
}
impl<T: Instance> embedded_hal_nb::serial::ErrorType for RingBufferedUartRx<'_, T> {
type Error = Error;
}
impl<T: Instance> ReadReady for RingBufferedUartRx<'_, T> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
let len = self.ring_buf.len().map_err(|e| match e {
crate::dma::ringbuffer::Error::Overrun => Self::Error::Overrun,
crate::dma::ringbuffer::Error::DmaUnsynced => {
error!(
"Ringbuffer error: DmaUNsynced, driver implementation is
probably bugged please open an issue"
);
Self::Error::Overrun
}
})?;
Ok(len > 0)
}
}