use core::convert::Infallible;
use crate::{
DEFAULT_RX_TRIGGER_LEVEL,
regs::{
self,
fields::{
FifoControl, InterruptEnable, InterruptId2, InterruptIdentification, LineStatus,
RxFifoTrigger,
},
},
};
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RxErrors {
parity: bool,
frame: bool,
overrun: bool,
}
impl RxErrors {
#[inline]
pub const fn new() -> Self {
Self {
parity: false,
frame: false,
overrun: false,
}
}
#[inline]
pub const fn parity(&self) -> bool {
self.parity
}
#[inline]
pub const fn frame(&self) -> bool {
self.frame
}
#[inline]
pub const fn overrun(&self) -> bool {
self.overrun
}
#[inline]
pub const fn has_errors(&self) -> bool {
self.parity || self.frame || self.overrun
}
}
pub struct Rx {
pub(crate) regs: regs::MmioRegisters<'static>,
pub(crate) errors: Option<RxErrors>,
rx_fifo_trigger: RxFifoTrigger,
}
impl Rx {
pub const unsafe fn steal(base_addr: usize) -> Self {
Self {
regs: unsafe { regs::Registers::new_mmio_at(base_addr) },
errors: None,
rx_fifo_trigger: DEFAULT_RX_TRIGGER_LEVEL,
}
}
pub(crate) fn new(regs: regs::MmioRegisters<'static>) -> Self {
Self {
regs,
errors: None,
rx_fifo_trigger: DEFAULT_RX_TRIGGER_LEVEL,
}
}
#[inline]
pub fn read_fifo(&mut self) -> nb::Result<u8, Infallible> {
let status_reg = self.regs.read_lsr();
if !status_reg.data_ready() {
return Err(nb::Error::WouldBlock);
}
if status_reg.error_in_rx_fifo() {
self.errors = Some(Self::lsr_to_errors(&status_reg));
}
Ok(self.read_fifo_unchecked())
}
#[inline(always)]
pub fn read_fifo_unchecked(&mut self) -> u8 {
self.regs.read_fifo_or_dll() as u8
}
#[inline]
pub fn start_interrupt_driven_reception(&mut self) {
self.reset_fifo();
self.enable_interrupt();
}
#[inline]
pub fn enable_interrupt(&mut self) {
self.regs.modify_ier_or_dlm(|val| {
let mut ier = InterruptEnable::new_with_raw_value(val);
ier.set_rx_avl(true);
ier.set_line_status(true);
ier.raw_value()
});
}
#[inline]
pub fn disable_interrupt(&mut self) {
self.regs.modify_ier_or_dlm(|val| {
let mut ier = InterruptEnable::new_with_raw_value(val);
ier.set_rx_avl(false);
ier.set_line_status(false);
ier.raw_value()
});
}
#[inline]
pub fn reset_fifo(&mut self) {
self.regs.write_iir_or_fcr(
FifoControl::builder()
.with_rx_fifo_trigger(self.rx_fifo_trigger)
.with_dma_mode_sel(false)
.with_reset_tx_fifo(false)
.with_reset_rx_fifo(true)
.with_fifo_enable(true)
.build()
.raw_value(),
);
}
#[inline(always)]
pub fn has_data(&self) -> bool {
self.regs.read_lsr().data_ready()
}
#[inline]
pub fn read_iir(&mut self) -> InterruptIdentification {
InterruptIdentification::new_with_raw_value(self.regs.read_iir_or_fcr())
}
#[inline]
pub fn on_interrupt_receiver_line_status(&mut self, _iir: InterruptIdentification) -> RxErrors {
let lsr = self.regs.read_lsr();
Self::lsr_to_errors(&lsr)
}
#[inline]
pub fn on_interrupt_data_available_or_char_timeout(
&mut self,
int_id2: InterruptId2,
buf: &mut [u8; 16],
) -> usize {
let mut read = 0;
if int_id2 == InterruptId2::RxDataAvailable {
(0..self.rx_fifo_trigger.as_num() as usize).for_each(|i| {
buf[i] = self.read_fifo_unchecked();
read += 1;
});
}
while self.has_data() && read < 16 {
buf[read] = self.read_fifo_unchecked();
read += 1;
}
read
}
pub fn lsr_to_errors(status_reg: &LineStatus) -> RxErrors {
let mut errors = RxErrors::new();
if status_reg.framing_error() {
errors.frame = true;
}
if status_reg.parity_error() {
errors.parity = true;
}
if status_reg.overrun_error() {
errors.overrun = true;
}
errors
}
}
impl embedded_hal_nb::serial::ErrorType for Rx {
type Error = Infallible;
}
impl embedded_hal_nb::serial::Read for Rx {
#[inline]
fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.read_fifo()
}
}
impl embedded_io::ErrorType for Rx {
type Error = Infallible;
}
impl embedded_io::Read for Rx {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
while !self.has_data() {}
let mut read = 0;
for byte in buf.iter_mut() {
match self.read_fifo() {
Ok(data) => {
*byte = data;
read += 1;
}
Err(nb::Error::WouldBlock) => break,
}
}
Ok(read)
}
}