use core::pin::Pin;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::{Context, Poll, Waker};
use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
use embassy_util::waitqueue::AtomicWaker;
use futures::Future;
use crate::pac::dma::vals;
use crate::{pac, peripherals};
pub fn copy<'a, C: Channel, W: Word>(ch: impl Peripheral
+ 'a, from: &[W], to: &mut [W]) -> Transfer<'a, C> {
assert!(from.len() == to.len());
into_ref!(ch);
unsafe {
let p = ch.regs();
p.read_addr().write_value(from.as_ptr() as u32);
p.write_addr().write_value(to.as_mut_ptr() as u32);
p.trans_count().write_value(from.len() as u32);
compiler_fence(Ordering::SeqCst);
p.ctrl_trig().write(|w| {
w.set_data_size(W::size());
w.set_incr_read(true);
w.set_incr_write(true);
w.set_chain_to(ch.number());
w.set_en(true);
});
compiler_fence(Ordering::SeqCst);
}
Transfer::new(ch)
}
pub(crate) struct Transfer<'a, C: Channel> {
channel: PeripheralRef<'a, C>,
}
impl<'a, C: Channel> Transfer<'a, C> {
pub(crate) fn new(channel: impl Peripheral
+ 'a) -> Self {
into_ref!(channel);
Self { channel }
}
}
impl<'a, C: Channel> Drop for Transfer<'a, C> {
fn drop(&mut self) {
let p = self.channel.regs();
unsafe {
p.ctrl_trig().write(|w| w.set_en(false));
while p.ctrl_trig().read().busy() {}
}
}
}
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
impl<'a, C: Channel> Future for Transfer<'a, C> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {
self.channel.set_waker(cx.waker());
if self.channel.is_running() {
Poll::Pending
} else {
Poll::Ready(())
}
}
}
struct ChannelState {
waker: AtomicWaker,
}
impl ChannelState {
const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
struct State {
channels: [ChannelState; 12],
}
impl State {
const fn new() -> Self {
const CH: ChannelState = ChannelState::new();
Self { channels: [CH; 12] }
}
}
static STATE: State = State::new();
mod sealed {
pub trait Channel {}
pub trait Word {}
}
pub trait Channel: Peripheral + sealed::Channel + Into + Sized + 'static {
fn number(&self) -> u8;
fn regs(&self) -> pac::dma::Channel {
pac::DMA.ch(self.number() as _)
}
fn is_running(&self) -> bool {
self.regs().ctrl_trig().read().en()
}
fn set_waker(&self, waker: &Waker) {
STATE.channels[self.number() as usize].waker.register(waker);
}
fn degrade(self) -> AnyChannel {
AnyChannel { number: self.number() }
}
}
pub trait Word: sealed::Word {
fn size() -> vals::DataSize;
}
impl sealed::Word for u8 {}
impl Word for u8 {
fn size() -> vals::DataSize {
vals::DataSize::SIZE_BYTE
}
}
impl sealed::Word for u16 {}
impl Word for u16 {
fn size() -> vals::DataSize {
vals::DataSize::SIZE_HALFWORD
}
}
impl sealed::Word for u32 {}
impl Word for u32 {
fn size() -> vals::DataSize {
vals::DataSize::SIZE_WORD
}
}
pub struct AnyChannel {
number: u8,
}
impl_peripheral!(AnyChannel);
impl sealed::Channel for AnyChannel {}
impl Channel for AnyChannel {
fn number(&self) -> u8 {
self.number
}
}
macro_rules! channel {
($name:ident, $num:expr) => {
impl sealed::Channel for peripherals::$name {}
impl Channel for peripherals::$name {
fn number(&self) -> u8 {
$num
}
}
impl From for crate::dma::AnyChannel {
fn from(val: peripherals::$name) -> Self {
crate::dma::Channel::degrade(val)
}
}
};
}
channel!(DMA_CH0, 0);
channel!(DMA_CH1, 1);
channel!(DMA_CH2, 2);
channel!(DMA_CH3, 3);
channel!(DMA_CH4, 4);
channel!(DMA_CH5, 5);
channel!(DMA_CH6, 6);
channel!(DMA_CH7, 7);
channel!(DMA_CH8, 8);
channel!(DMA_CH9, 9);
channel!(DMA_CH10, 10);
channel!(DMA_CH11, 11);