imxrt_dma/
interrupt.rs

1//! DMA interrupt support
2
3use crate::{channel::Channel, Error};
4use core::{
5    cell::RefCell,
6    future::Future,
7    marker::PhantomPinned,
8    pin::Pin,
9    sync::atomic,
10    task::{Context, Poll, Waker},
11};
12
13use cortex_m::interrupt::{self, Mutex};
14
15impl<const CHANNELS: usize> super::Dma<CHANNELS> {
16    /// Handle a DMA interrupt
17    ///
18    /// Checks the interrupt status for the channel identified by `channel`.
19    /// If the channel completed its transfer, `on_interrupt` wakes the channel's
20    /// waker.
21    ///
22    /// Consider calling `on_interrupt` in a DMA channel's interrupt handler:
23    ///
24    /// ```
25    /// use imxrt_dma::Dma;
26    /// static DMA: Dma<32> = // Handle to DMA driver.
27    /// # unsafe { Dma::new(core::ptr::null(), core::ptr::null()) };
28    ///
29    /// // #[cortex_m_rt::interrupt]
30    /// fn DMA7_DMA23() {
31    ///     // Safety: only checking channels 7 and 23, which
32    ///     // are both valid on an i.MX RT 1060 chip.
33    ///     unsafe {
34    ///         DMA.on_interrupt(7);
35    ///         DMA.on_interrupt(23);
36    ///     }
37    /// }
38    /// ```
39    ///
40    /// # Safety
41    ///
42    /// This should only be used when the associated DMA channel is exclusively referenced
43    /// by a DMA transfer future. Caller must ensure that `on_interrupt` is called in
44    /// the correct interrupt handler.
45    ///
46    /// # Panics
47    ///
48    /// Panics if `channel` is greater than or equal to the maximum number of channels.
49    #[inline(always)]
50    pub unsafe fn on_interrupt(&'static self, channel: usize) {
51        let channel = self.channel(channel);
52        if channel.is_interrupt() {
53            channel.clear_interrupt();
54        }
55
56        if channel.is_complete() | channel.is_error() {
57            interrupt::free(|cs| {
58                let waker = self.wakers[channel.channel()].borrow(cs);
59                let mut waker = waker.borrow_mut();
60                if let Some(waker) = waker.take() {
61                    waker.wake();
62                }
63            });
64        }
65    }
66}
67
68pub(crate) type SharedWaker = Mutex<RefCell<Option<Waker>>>;
69#[allow(clippy::declare_interior_mutable_const)] // Very convenient, and usage for static init deemed OK in clippy docs
70pub(crate) const NO_WAKER: SharedWaker = Mutex::new(RefCell::new(None));
71
72/// The core DMA transfer future
73///
74/// `Transfer` is a future that drives the DMA transfer. `Transfer` will
75/// initiate a DMA transfer when it is first polled. You may then poll it
76/// to understand when the transfer completes.
77///
78/// To cancel a transfer, drop the `Transfer`.
79///
80/// If you've enabled DMA interrupts, consider using [`on_interrupt`](crate::Dma::on_interrupt)
81/// to wake an executor when the DMA transfer completes, The interrupt interface assumes that you've
82///
83/// - configured your channel to generate interrupts
84/// - registered a DMA ISR with your embedded runtime
85/// - unmasked the DMA interrupt in the NVIC
86///
87/// `Transfer` calls the unsafe [`enable`](crate::channel::Channel::enable) method to enable a
88/// DMA transfer. To properly use `Transfer`, make sure that you've configured your DMA
89/// channel for a valid transfer.
90///
91/// `Transfer` is the core DMA future used in `imxrt_dma`. For safe DMA transfers, consider
92/// using
93///
94/// - [`Memcpy`](crate::memcpy::Memcpy) for buffer-to-buffer DMA transfers
95/// - [`Read`](crate::peripheral::Read) for peripheral-to-memory DMA transfers
96/// - [`Write`](crate::peripheral::Write) for memory-to-peripheral DMA transfers
97///
98/// `Transfer` is designed to the DMA `Channel` public interface. If you need to implement
99/// your own transfer future, you may do so.
100///
101/// ```no_run
102/// use imxrt_dma::{channel::Channel, Transfer};
103///
104/// # static DMA: imxrt_dma::Dma<32> = unsafe { imxrt_dma::Dma::new(core::ptr::null(), core::ptr::null()) };
105/// # async fn f() -> imxrt_dma::Result<()> {
106/// let my_channel: Channel = // Acquire your channel...
107///     # unsafe { DMA.channel(0) };
108/// // Properly prepare your transfer...
109/// // Safety: transfer properly prepared
110/// unsafe { Transfer::new(&my_channel) }.await?;
111/// # Ok(()) }
112/// ```
113pub struct Transfer<'a> {
114    channel: &'a Channel,
115    _pinned: PhantomPinned,
116}
117
118impl<'a> Transfer<'a> {
119    /// Create a new `Transfer` that performs the DMA transfer described by `channel`
120    ///
121    /// # Safety
122    ///
123    /// Assumes that the transfer is correctly defined in the DMA channel memory.
124    /// The transfer enables after the first call to `poll()`.
125    pub unsafe fn new(channel: &'a Channel) -> Self {
126        Transfer {
127            channel,
128            _pinned: PhantomPinned,
129        }
130    }
131}
132
133impl Future for Transfer<'_> {
134    type Output = Result<(), Error>;
135    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
136        interrupt::free(|cs| {
137            let waker = self.channel.waker.borrow(cs);
138            let mut waker = waker.borrow_mut();
139            *waker = Some(cx.waker().clone());
140        });
141
142        loop {
143            // This driver is only expecting to catch synchronous errors
144            // (those that manifest once we enable the transfer). If there
145            // is a misconfiguration that only the hardware detects, we expect
146            // to see it as soon as we loop back around after the enable.
147            if self.channel.is_error() {
148                let es = self.channel.error_status();
149                self.channel.clear_error();
150                return Poll::Ready(Err(es));
151            } else if self.channel.is_complete() {
152                self.channel.clear_complete();
153                return Poll::Ready(Ok(()));
154            } else if self.channel.is_enabled() {
155                return Poll::Pending;
156            } else {
157                atomic::fence(atomic::Ordering::SeqCst);
158                unsafe { self.channel.enable() };
159            }
160        }
161    }
162}
163
164impl Drop for Transfer<'_> {
165    fn drop(&mut self) {
166        self.channel.disable();
167        self.channel.clear_complete();
168        self.channel.clear_error();
169        interrupt::free(|cs| {
170            let waker = self.channel.waker.borrow(cs);
171            let mut waker = waker.borrow_mut();
172            *waker = None;
173        });
174    }
175}