use imxrt_hal::{
dma::{channel, peripheral::Destination},
lpuart::{Direction, Lpuart},
};
use static_cell::StaticCell;
pub(crate) struct Backend {
consumer: crate::Consumer,
channel: channel::Channel,
}
impl Backend {
pub(crate) fn poll(&mut self) {
while self.channel.is_interrupt() {
self.channel.clear_interrupt();
}
assert!(
!self.channel.is_error(),
"{:?}",
self.channel.error_status()
);
if self.channel.is_enabled() {
return;
}
let complete = {
let mut complete = false;
while self.channel.is_complete() {
self.channel.clear_complete();
complete = true;
}
complete
};
if let Ok(grant) = self.consumer.read() {
let (completed, new) = if complete {
let transferred: usize = self.channel.beginning_transfer_iterations().into();
let buf = grant.buf();
(&buf[..transferred], &buf[transferred..])
} else {
(&[][..], grant.buf())
};
if !new.is_empty() {
unsafe { channel::set_source_linear_buffer(&mut self.channel, new) };
unsafe {
self.channel
.set_transfer_iterations(new.len().min(u16::MAX as usize) as u16)
};
unsafe { self.channel.enable() };
}
if !completed.is_empty() {
let completed = completed.len();
grant.release(completed);
}
}
}
}
pub(crate) fn init(
mut lpuart: Lpuart,
mut channel: channel::Channel,
consumer: crate::Consumer,
interrupts: crate::Interrupts,
) -> &'static mut Backend {
channel.disable();
channel.clear_complete();
channel.clear_error();
static BACKEND: StaticCell<Backend> = StaticCell::new();
BACKEND.init_with(move || {
channel.set_disable_on_completion(true);
channel.set_interrupt_on_completion(interrupts == crate::Interrupts::Enabled);
channel
.set_channel_configuration(channel::Configuration::enable(lpuart.destination_signal()));
unsafe { channel.set_minor_loop_bytes(core::mem::size_of::<u8>() as u32) };
unsafe { channel::set_destination_hardware(&mut channel, lpuart.destination_address()) };
lpuart.disable(|lpuart| {
lpuart.disable_fifo(Direction::Tx);
});
lpuart.enable_destination();
Backend { channel, consumer }
})
}