use crate::{Error, channel::Channel};
use core::{
cell::RefCell,
future::Future,
marker::PhantomPinned,
pin::Pin,
sync::atomic,
task::{Context, Poll, Waker},
};
use critical_section::Mutex;
impl<const CHANNELS: usize> super::Dma<CHANNELS> {
#[inline(always)]
pub unsafe fn on_interrupt(&'static self, channel: usize) {
let channel = unsafe { self.channel(channel) };
if channel.is_interrupt() {
channel.clear_interrupt();
}
if channel.is_complete() | channel.is_error() {
critical_section::with(|cs| {
let waker = self.wakers[channel.channel()].borrow(cs);
let mut waker = waker.borrow_mut();
if let Some(waker) = waker.take() {
waker.wake();
}
});
}
}
}
pub(crate) type SharedWaker = Mutex<RefCell<Option<Waker>>>;
#[allow(clippy::declare_interior_mutable_const)] pub(crate) const NO_WAKER: SharedWaker = Mutex::new(RefCell::new(None));
pub struct Transfer<'a> {
channel: &'a Channel,
_pinned: PhantomPinned,
}
impl<'a> Transfer<'a> {
pub unsafe fn new(channel: &'a Channel) -> Self {
Transfer {
channel,
_pinned: PhantomPinned,
}
}
}
impl Future for Transfer<'_> {
type Output = Result<(), Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
critical_section::with(|cs| {
let waker = self.channel.waker.borrow(cs);
let mut waker = waker.borrow_mut();
*waker = Some(cx.waker().clone());
});
loop {
if self.channel.is_error() {
let es = self.channel.error_status();
self.channel.clear_error();
return Poll::Ready(Err(es));
} else if self.channel.is_complete() {
self.channel.clear_complete();
return Poll::Ready(Ok(()));
} else if self.channel.is_enabled() {
return Poll::Pending;
} else {
atomic::fence(atomic::Ordering::SeqCst);
unsafe { self.channel.enable() };
}
}
}
}
impl Drop for Transfer<'_> {
fn drop(&mut self) {
self.channel.disable();
self.channel.clear_complete();
self.channel.clear_error();
critical_section::with(|cs| {
let waker = self.channel.waker.borrow(cs);
let mut waker = waker.borrow_mut();
*waker = None;
});
}
}