atsamd_hal/dmac/channel/
mod.rs

1//! # Abstractions over individual DMA channels
2//!
3//! # Initializing
4//!
5//! Individual channels should be initialized through the
6//! [`Channel::init`] method. This will return a `Channel<Id, Ready>` ready for
7//! use by a [`Transfer`]. Initializing a channel requires setting a priority
8//! level, as well as enabling or disabling interrupt requests (only for the
9//! specific channel being initialized).
10//!
11//! # Burst Length and FIFO Threshold (SAMD51/SAME5x only)
12//!
13//! The transfer burst length can be configured through the
14//! [`Channel::burst_length`] method. A burst is an atomic,
15//! uninterruptible transfer which length corresponds to a number of beats. See
16//! SAMD5x/E5x datasheet section 22.6.1.1 for more information. The FIFO
17//! threshold can be configured through the
18//! [`Channel::fifo_threshold`] method. This enables the channel
19//! to wait for multiple Beats before sending a Burst. See SAMD5x/E5x datasheet
20//! section 22.6.2.8 for more information.
21//!
22//! # Channel status
23//!
24//! Channels can be in any of three statuses: [`Uninitialized`], [`Ready`], and
25//! [`Busy`]. These statuses are checked at compile time to ensure they are
26//! properly initialized before launching DMA transfers.
27//!
28//! # Resetting
29//!
30//! Calling the [`Channel::reset`] method will reset the channel to its
31//! `Uninitialized` state. You will be required to call [`Channel::init`]
32//! again before being able to use it with a `Transfer`.
33
34#![allow(unused_braces)]
35
36use core::marker::PhantomData;
37use core::sync::atomic;
38
39use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
40
41use super::{
42    dma_controller::{ChId, PriorityLevel, TriggerAction, TriggerSource},
43    sram::{self, DmacDescriptor},
44    transfer::{BufferPair, Transfer},
45    Beat, Buffer, Error,
46};
47use crate::typelevel::{Is, Sealed};
48use modular_bitfield::prelude::*;
49
50mod reg;
51use reg::RegisterBlock;
52
53#[hal_cfg("dmac-d5x")]
54use super::dma_controller::{BurstLength, FifoThreshold};
55
56//==============================================================================
57// Channel Status
58//==============================================================================
59pub trait Status: Sealed {
60    type Uninitialized: Status;
61    type Ready: Status;
62}
63
64/// Uninitialized channel
65pub enum Uninitialized {}
66impl Sealed for Uninitialized {}
67impl Status for Uninitialized {
68    type Uninitialized = Uninitialized;
69    type Ready = Ready;
70}
71
72/// Initialized and ready to transfer channel
73pub enum Ready {}
74impl Sealed for Ready {}
75impl Status for Ready {
76    type Uninitialized = Uninitialized;
77    type Ready = Ready;
78}
79
80/// Busy channel
81pub enum Busy {}
82impl Sealed for Busy {}
83impl Status for Busy {
84    type Uninitialized = Uninitialized;
85    type Ready = Ready;
86}
87
88/// Uninitialized [`Channel`] configured for `async` operation
89#[cfg(feature = "async")]
90pub enum UninitializedFuture {}
91#[cfg(feature = "async")]
92impl Sealed for UninitializedFuture {}
93#[cfg(feature = "async")]
94impl Status for UninitializedFuture {
95    type Uninitialized = UninitializedFuture;
96    type Ready = ReadyFuture;
97}
98
99/// Initialized and ready to transfer in `async` operation
100#[cfg(feature = "async")]
101pub enum ReadyFuture {}
102#[cfg(feature = "async")]
103impl Sealed for ReadyFuture {}
104#[cfg(feature = "async")]
105impl Status for ReadyFuture {
106    type Uninitialized = UninitializedFuture;
107    type Ready = ReadyFuture;
108}
109
110pub trait ReadyChannel: Status {}
111impl ReadyChannel for Ready {}
112#[cfg(feature = "async")]
113impl ReadyChannel for ReadyFuture {}
114
115//==============================================================================
116// AnyChannel
117//==============================================================================
118pub trait AnyChannel: Sealed + Is<Type = SpecificChannel<Self>> {
119    type Status: Status;
120    type Id: ChId;
121}
122
123pub type SpecificChannel<C> = Channel<<C as AnyChannel>::Id, <C as AnyChannel>::Status>;
124
125pub type ChannelStatus<C> = <C as AnyChannel>::Status;
126pub type ChannelId<C> = <C as AnyChannel>::Id;
127
128impl<Id, S> Sealed for Channel<Id, S>
129where
130    Id: ChId,
131    S: Status,
132{
133}
134
135impl<Id, S> AnyChannel for Channel<Id, S>
136where
137    Id: ChId,
138    S: Status,
139{
140    type Id = Id;
141    type Status = S;
142}
143
144impl<Id, S> AsRef<Self> for Channel<Id, S>
145where
146    Id: ChId,
147    S: Status,
148{
149    #[inline]
150    fn as_ref(&self) -> &Self {
151        self
152    }
153}
154
155impl<Id, S> AsMut<Self> for Channel<Id, S>
156where
157    Id: ChId,
158    S: Status,
159{
160    #[inline]
161    fn as_mut(&mut self) -> &mut Self {
162        self
163    }
164}
165
166//==============================================================================
167// Channel
168//==============================================================================
169
170/// DMA channel, capable of executing
171/// [`Transfer`]s. Ongoing DMA transfers are automatically stopped when a
172/// [`Channel`] is dropped.
173pub struct Channel<Id: ChId, S: Status> {
174    regs: RegisterBlock<Id>,
175    _status: PhantomData<S>,
176}
177
178#[inline]
179pub(super) fn new_chan<Id: ChId>(_id: PhantomData<Id>) -> Channel<Id, Uninitialized> {
180    Channel {
181        regs: RegisterBlock::new(_id),
182        _status: PhantomData,
183    }
184}
185
186#[cfg(feature = "async")]
187#[inline]
188pub(super) fn new_chan_future<Id: ChId>(_id: PhantomData<Id>) -> Channel<Id, UninitializedFuture> {
189    Channel {
190        regs: RegisterBlock::new(_id),
191        _status: PhantomData,
192    }
193}
194
195/// These methods may be used on any DMA channel in any configuration
196impl<Id: ChId, S: Status> Channel<Id, S> {
197    /// Configure the DMA channel so that it is ready to be used by a
198    /// [`Transfer`](super::transfer::Transfer).
199    ///
200    /// # Return
201    ///
202    /// A `Channel` with a `Ready` status
203    #[inline]
204    #[hal_macro_helper]
205    pub fn init(mut self, lvl: PriorityLevel) -> Channel<Id, S::Ready> {
206        // Software reset the channel for good measure
207        self._reset_private();
208
209        #[hal_cfg(any("dmac-d11", "dmac-d21"))]
210        // Setup priority level
211        self.regs.chctrlb.modify(|_, w| w.lvl().variant(lvl));
212
213        #[hal_cfg("dmac-d5x")]
214        self.regs.chprilvl.modify(|_, w| w.prilvl().variant(lvl));
215
216        self.change_status()
217    }
218
219    /// Selectively enable interrupts
220    #[inline]
221    pub fn enable_interrupts(&mut self, flags: InterruptFlags) {
222        // SAFETY: This is safe as InterruptFlags is only capable of writing in
223        // non-reserved bits
224        self.regs
225            .chintenset
226            .write(|w| unsafe { w.bits(flags.into()) });
227    }
228
229    /// Selectively disable interrupts
230    #[inline]
231    pub fn disable_interrupts(&mut self, flags: InterruptFlags) {
232        // SAFETY: This is safe as InterruptFlags is only capable of writing in
233        // non-reserved bits
234        self.regs
235            .chintenclr
236            .write(|w| unsafe { w.bits(flags.into()) });
237    }
238
239    /// Check the specified `flags`, clear then return any that were set
240    #[inline]
241    pub fn check_and_clear_interrupts(&mut self, flags: InterruptFlags) -> InterruptFlags {
242        let mut cleared = 0;
243        self.regs.chintflag.modify(|r, w| {
244            cleared = r.bits() & flags.into_bytes()[0];
245            unsafe { w.bits(cleared) }
246        });
247
248        InterruptFlags::from_bytes([cleared])
249    }
250
251    #[inline]
252    pub(super) fn change_status<N: Status>(self) -> Channel<Id, N> {
253        Channel {
254            regs: self.regs,
255            _status: PhantomData,
256        }
257    }
258
259    #[inline]
260    fn _reset_private(&mut self) {
261        // Reset the channel to its startup state and wait for reset to complete
262        self.regs.chctrla.modify(|_, w| w.swrst().set_bit());
263        while self.regs.chctrla.read().swrst().bit_is_set() {}
264    }
265
266    #[inline]
267    fn _trigger_private(&mut self) {
268        self.regs.swtrigctrl.set_bit();
269    }
270
271    /// Enable the transfer, and emit a compiler fence.
272    #[inline]
273    fn _enable_private(&mut self) {
274        // Prevent the compiler from re-ordering read/write
275        // operations beyond this fence.
276        // (see https://docs.rust-embedded.org/embedonomicon/dma.html#compiler-misoptimizations)
277        atomic::fence(atomic::Ordering::Release); // ▲
278        self.regs.chctrla.modify(|_, w| w.enable().set_bit());
279    }
280
281    /// Stop transfer on channel whether or not the transfer has completed
282    #[inline]
283    pub(crate) fn stop(&mut self) {
284        self.regs.chctrla.modify(|_, w| w.enable().clear_bit());
285
286        // Wait for the burst to finish
287        while !self.xfer_complete() {
288            core::hint::spin_loop();
289        }
290
291        // Prevent the compiler from re-ordering read/write
292        // operations beyond this fence.
293        // (see https://docs.rust-embedded.org/embedonomicon/dma.html#compiler-misoptimizations)
294        atomic::fence(atomic::Ordering::Acquire); // ▼
295    }
296
297    /// Returns whether or not the transfer is complete.
298    #[inline]
299    pub(crate) fn xfer_complete(&mut self) -> bool {
300        self.regs.chctrla.read().enable().bit_is_clear()
301    }
302
303    /// Returns the transfer's success status.
304    #[allow(dead_code)]
305    #[inline]
306    pub(crate) fn xfer_success(&mut self) -> super::Result<()> {
307        let success = self.regs.chintflag.read().terr().bit_is_clear();
308        success.then_some(()).ok_or(Error::TransferError)
309    }
310
311    /// Return a mutable reference to the DMAC descriptor that belongs to this
312    /// channel. In the case of linked transfers, this will be the first
313    /// descriptor in the chain.
314    #[inline]
315    fn descriptor_mut(&mut self) -> &mut DmacDescriptor {
316        // SAFETY this is only safe as long as we read/write to the descriptor
317        // belonging to OUR channel. We assume this is the case, as there can only ever
318        // exist one (safely created) instance of Self, and we're taking an exclusive
319        // reference to Self.
320        unsafe {
321            let id = ChannelId::<Self>::USIZE;
322            &mut *sram::get_descriptor(id)
323        }
324    }
325
326    /// Fill the first descriptor of a channel into the SRAM descriptor section.
327    ///
328    /// # Safety
329    ///
330    /// This method may only be called on a channel which is not actively being
331    /// used for transferring data.
332    #[inline]
333    pub(super) unsafe fn fill_descriptor<Src: Buffer, Dst: Buffer<Beat = Src::Beat>>(
334        &mut self,
335        source: &mut Src,
336        destination: &mut Dst,
337        circular: bool,
338    ) {
339        let descriptor = self.descriptor_mut();
340
341        // Enable support for circular transfers. If circular_xfer is true,
342        // we set the address of the "next" block descriptor to actually
343        // be the same address as the current block descriptor.
344        // Otherwise we set it to NULL, which terminates the transaction.
345        let descaddr = if circular {
346            // SAFETY This is safe as we are only reading the descriptor's address,
347            // and not actually writing any data to it. We also assume the descriptor
348            // will never be moved.
349            descriptor as *mut _
350        } else {
351            core::ptr::null_mut()
352        };
353
354        write_descriptor(descriptor, source, destination, descaddr);
355    }
356
357    /// Add a linked descriptor after the first descriptor in the transfer.
358    ///
359    /// # Safety
360    ///
361    /// * This method may only be called on a channel which is not actively
362    ///   being used for transferring data.
363    ///
364    /// * `next` must point to a valid [`DmacDescriptor`], with all the safety
365    ///   considerations that entails: the source and destination buffers must
366    ///   be valid, have compatible lengths, remain in scope for the entirety of
367    ///   the transfer, etc.
368    ///
369    /// * The pointer in the `descaddr` field of `next`, along with the
370    ///   descriptor it points to, etc, must point to a valid [`DmacDescriptor`]
371    ///   memory location, or be null. They must not be circular (ie, points to
372    ///   itself). Any linked transfer must strictly be a read transaction
373    ///   (destination pointer is a byte buffer, source pointer is the SERCOM
374    ///   DATA register).
375    pub(super) unsafe fn link_next(&mut self, next: *mut DmacDescriptor) {
376        self.descriptor_mut().descaddr = next;
377    }
378}
379
380impl<Id, R> Channel<Id, R>
381where
382    Id: ChId,
383    R: ReadyChannel,
384{
385    /// Issue a software reset to the channel. This will return the channel to
386    /// its startup state
387    #[inline]
388    pub fn reset(mut self) -> Channel<Id, R::Uninitialized> {
389        self._reset_private();
390        self.change_status()
391    }
392
393    /// Set the FIFO threshold length. The channel will wait until it has
394    /// received the selected number of Beats before triggering the Burst
395    /// transfer, reducing the DMA transfer latency.
396    #[hal_cfg("dmac-d5x")]
397    #[inline]
398    pub fn fifo_threshold(&mut self, threshold: FifoThreshold) {
399        self.regs
400            .chctrla
401            .modify(|_, w| w.threshold().variant(threshold));
402    }
403
404    #[cfg(doc)]
405    #[hal_cfg(not("dmac-d5x"))]
406    /// This method is not present with the selected feature set, defined for
407    /// documentation only
408    pub fn fifo_threshold(&mut self) {
409        unimplemented!()
410    }
411
412    /// Set burst length for the channel, in number of beats. A burst transfer
413    /// is an atomic, uninterruptible operation.
414    #[hal_cfg("dmac-d5x")]
415    #[inline]
416    pub fn burst_length(&mut self, burst_length: BurstLength) {
417        self.regs
418            .chctrla
419            .modify(|_, w| w.burstlen().variant(burst_length));
420    }
421
422    #[cfg(doc)]
423    #[hal_cfg(not("dmac-d5x"))]
424    /// This method is not present with the selected feature set, defined for
425    /// documentation only
426    pub fn burst_length(&mut self) {
427        unimplemented!()
428    }
429
430    /// Start the transfer.
431    ///
432    /// # Safety
433    ///
434    /// This function is unsafe because it starts the transfer without changing
435    /// the channel status to [`Busy`]. A [`Ready`] channel which is actively
436    /// transferring should NEVER be leaked.
437    #[inline]
438    #[hal_macro_helper]
439    pub(super) unsafe fn _start_private(
440        &mut self,
441        trig_src: TriggerSource,
442        trig_act: TriggerAction,
443    ) {
444        // Configure the trigger source and trigger action
445        self.configure_trigger(trig_src, trig_act);
446        self._enable_private();
447
448        // If trigger source is DISABLE, manually trigger transfer
449        if trig_src == TriggerSource::Disable {
450            self._trigger_private();
451        }
452    }
453
454    #[inline]
455    #[hal_macro_helper]
456    pub(super) fn configure_trigger(&mut self, trig_src: TriggerSource, trig_act: TriggerAction) {
457        // Configure the trigger source and trigger action
458        #[hal_cfg(any("dmac-d11", "dmac-d21"))]
459        self.regs.chctrlb.modify(|_, w| {
460            w.trigsrc().variant(trig_src);
461            w.trigact().variant(trig_act)
462        });
463
464        #[hal_cfg("dmac-d5x")]
465        self.regs.chctrla.modify(|_, w| {
466            w.trigsrc().variant(trig_src);
467            w.trigact().variant(trig_act)
468        });
469    }
470}
471
472impl<Id: ChId> Channel<Id, Ready> {
473    /// Start transfer on channel using the specified trigger source.
474    ///
475    /// # Return
476    ///
477    /// A `Channel` with a `Busy` status.
478    #[inline]
479    pub(crate) fn start(
480        mut self,
481        trig_src: TriggerSource,
482        trig_act: TriggerAction,
483    ) -> Channel<Id, Busy> {
484        unsafe {
485            self._start_private(trig_src, trig_act);
486        }
487        self.change_status()
488    }
489
490    /// Begin a [`Transfer`], without changing the channel's type to [`Busy`].
491    ///
492    /// This method provides an additional safety guarantee over
493    /// [`Self::transfer_unchecked`]; it checks that the buffer lengths are
494    /// valid before attempting to start the transfer.
495    ///
496    /// Also provides support for linked transfers via an optional `&mut
497    /// DmacDescriptor`.
498    ///
499    /// This function guarantees that it will never return [`Err`] if the
500    /// transfer has been started.
501    ///
502    /// # Safety
503    ///
504    /// * You must ensure that the transfer is completed or stopped before
505    ///   returning the [`Channel`]. Doing otherwise breaks type safety, because
506    ///   a [`Ready`] channel would still be in the middle of a transfer.
507    /// * If the provided `linked_descriptor` is `Some` it must not be dropped
508    ///   until the transfer is completed or stopped.
509    /// * Additionnally, this function doesn't take `'static` buffers. Again,
510    ///   you must guarantee that the returned transfer has completed or has
511    ///   been stopped before giving up control of the underlying [`Channel`].
512    #[inline]
513    #[allow(dead_code)]
514    pub(crate) unsafe fn transfer<S, D>(
515        &mut self,
516        source: &mut S,
517        dest: &mut D,
518        trig_src: TriggerSource,
519        trig_act: TriggerAction,
520        linked_descriptor: Option<&mut DmacDescriptor>,
521    ) -> Result<(), Error>
522    where
523        S: Buffer,
524        D: Buffer<Beat = S::Beat>,
525    {
526        Transfer::<Self, BufferPair<S, D>>::check_buffer_pair(source, dest)?;
527        self.transfer_unchecked(source, dest, trig_src, trig_act, linked_descriptor);
528        Ok(())
529    }
530
531    /// Begin a transfer, without changing the channel's type to [`Busy`].
532    ///
533    /// Also provides support for linked transfers via an optional `&mut
534    /// DmacDescriptor`.
535    ///
536    /// # Safety
537    ///
538    /// * This method does not check that the two provided buffers have
539    ///   compatible lengths. You must guarantee that:
540    ///   - Either `source` or `dest` has a buffer length of 1, or
541    ///   - Both buffers have the same length.
542    /// * You must ensure that the transfer is completed or stopped before
543    ///   returning the [`Channel`]. Doing otherwise breaks type safety, because
544    ///   a [`Ready`] channel would still be in the middle of a transfer.
545    /// * If the provided `linked_descriptor` is `Some` it must not be dropped
546    ///   until the transfer is completed or stopped.
547    /// * Additionnally, this function doesn't take `'static` buffers. Again,
548    ///   you must guarantee that the returned transfer has completed or has
549    ///   been stopped before giving up control of the underlying [`Channel`].
550    #[inline]
551    pub(crate) unsafe fn transfer_unchecked<S, D>(
552        &mut self,
553        source: &mut S,
554        dest: &mut D,
555        trig_src: TriggerSource,
556        trig_act: TriggerAction,
557        linked_descriptor: Option<&mut DmacDescriptor>,
558    ) where
559        S: Buffer,
560        D: Buffer<Beat = S::Beat>,
561    {
562        self.fill_descriptor(source, dest, false);
563
564        if let Some(next) = linked_descriptor {
565            self.link_next(next as *mut _);
566        }
567
568        self.configure_trigger(trig_src, trig_act);
569        self._enable_private();
570
571        if trig_src == TriggerSource::Disable {
572            self._trigger_private();
573        }
574    }
575}
576
577/// These methods may only be used on a `Busy` DMA channel
578impl<Id: ChId> Channel<Id, Busy> {
579    /// Issue a software trigger to the channel
580    #[inline]
581    pub(crate) fn software_trigger(&mut self) {
582        self._trigger_private();
583    }
584
585    /// Stop transfer on channel whether or not the transfer has completed, and
586    /// return the resources it holds.
587    ///
588    /// # Return
589    ///
590    /// A `Channel` with a `Ready` status, ready to be reused by a new
591    /// [`Transfer`](super::transfer::Transfer)
592    #[inline]
593    pub(crate) fn free(mut self) -> Channel<Id, Ready> {
594        self.stop();
595        self.change_status()
596    }
597
598    /// Restart transfer using previously-configured trigger source and action
599    #[inline]
600    pub(crate) fn restart(&mut self) {
601        self._enable_private();
602    }
603}
604
605impl<Id: ChId> From<Channel<Id, Ready>> for Channel<Id, Uninitialized> {
606    fn from(mut item: Channel<Id, Ready>) -> Self {
607        item._reset_private();
608        item.change_status()
609    }
610}
611
612#[cfg(feature = "async")]
613impl<Id: ChId> Channel<Id, ReadyFuture> {
614    /// Begin DMA transfer using `async` operation.
615    ///
616    /// If [`TriggerSource::Disable`] is used, a software
617    /// trigger will be issued to the DMA channel to launch the transfer. It
618    /// is therefore not necessary, in most cases, to manually issue a
619    /// software trigger to the channel.
620    ///
621    /// # Safety
622    ///
623    /// In `async` mode, a transfer does NOT require `'static` source and
624    /// destination buffers. This, in theory, makes
625    /// [`transfer_future`](Channel::transfer_future) an `unsafe` function,
626    /// although it is marked as safe for better ergonomics.
627    ///
628    /// This means that, as an user, you **must** ensure that the [`Future`]
629    /// returned by this function may never be forgotten through [`forget`] or
630    /// by wrapping it with a [`ManuallyDrop`].
631    ///
632    /// The returned future implements [`Drop`] and will automatically stop any
633    /// ongoing transfers to guarantee that the memory occupied by the
634    /// now-dropped buffers may not be corrupted by running transfers. This
635    /// also means that should you [`forget`] this [`Future`] after its
636    /// first [`poll`] call, the transfer will keep running, ruining the
637    /// now-reclaimed memory, as well as the rest of your day.
638    ///
639    /// * `await`ing is fine: the [`Future`] will run to completion.
640    /// * Dropping an incomplete transfer is also fine. Dropping can happen, for
641    ///   example, if the transfer doesn't complete before a timeout expires.
642    ///
643    /// [`forget`]: core::mem::forget
644    /// [`ManuallyDrop`]: core::mem::ManuallyDrop
645    /// [`Future`]: core::future::Future
646    /// [`poll`]: core::future::Future::poll
647    #[inline]
648    pub async fn transfer_future<S, D>(
649        &mut self,
650        mut source: S,
651        mut dest: D,
652        trig_src: TriggerSource,
653        trig_act: TriggerAction,
654    ) -> Result<(), super::Error>
655    where
656        S: super::Buffer,
657        D: super::Buffer<Beat = S::Beat>,
658    {
659        unsafe {
660            self.transfer_future_linked(&mut source, &mut dest, trig_src, trig_act, None)
661                .await
662        }
663    }
664
665    /// Begin an async transfer, without changing the channel's type to
666    /// [`Busy`].
667    ///
668    /// Also provides support for linked transfers via an optional `&mut
669    /// DmacDescriptor`.
670    ///
671    /// # Safety
672    ///
673    /// * This method does not check that the two provided buffers have
674    ///   compatible lengths. You must guarantee that:
675    ///   - Either `source` or `dest` has a buffer length of 1, or
676    ///   - Both buffers have the same length.
677    /// * You must ensure that the transfer is completed or stopped before
678    ///   returning the [`Channel`]. Doing otherwise breaks type safety, because
679    ///   a [`ReadyFuture`] channel would still be in the middle of a transfer.
680    /// * If the provided `linked_descriptor` is `Some` it must not be dropped
681    ///   until the transfer is completed or stopped.
682    /// * Additionnally, this function doesn't take `'static` buffers. Again,
683    ///   you must guarantee that the returned transfer has completed or has
684    ///   been stopped before giving up control of the underlying [`Channel`].
685    pub(crate) async unsafe fn transfer_future_linked<S, D>(
686        &mut self,
687        source: &mut S,
688        dest: &mut D,
689        trig_src: TriggerSource,
690        trig_act: TriggerAction,
691        linked_descriptor: Option<&mut DmacDescriptor>,
692    ) -> Result<(), super::Error>
693    where
694        S: super::Buffer,
695        D: super::Buffer<Beat = S::Beat>,
696    {
697        super::Transfer::<Self, super::transfer::BufferPair<S, D>>::check_buffer_pair(
698            source, dest,
699        )?;
700        unsafe {
701            self.fill_descriptor(source, dest, false);
702            if let Some(next) = linked_descriptor {
703                self.link_next(next as *mut _);
704            }
705        }
706
707        self.disable_interrupts(
708            InterruptFlags::new()
709                .with_susp(true)
710                .with_tcmpl(true)
711                .with_terr(true),
712        );
713
714        self.configure_trigger(trig_src, trig_act);
715
716        transfer_future::TransferFuture::new(self, trig_src).await;
717
718        // No need to defensively disable channel here; it's automatically stopped when
719        // TransferFuture is dropped. Even though `stop()` is implicitly called
720        // through TransferFuture::drop, it *absolutely* must be called before
721        // this function is returned, because it emits the compiler fence which ensures
722        // memory operations aren't reordered beyond the DMA transfer's bounds.
723
724        // TODO currently this will always return Ok(()) since we unconditionally clear
725        // ERROR
726        self.xfer_success()
727    }
728}
729
730#[cfg(feature = "async")]
731mod transfer_future {
732    use super::*;
733
734    /// [`Future`](core::future::Future) which starts, then waits on a DMA
735    /// transfer.
736    ///
737    /// This implementation is a standalone struct instead of using
738    /// [`poll_fn`](core::future::poll_fn), because we want to implement
739    /// [`Drop`] for the future returned by the
740    /// [`transfer_future`](super::Channel::transfer_future) method. This way we
741    /// can stop transfers when they are dropped, thus avoiding undefined
742    /// behaviour.
743    pub(super) struct TransferFuture<'a, Id: ChId> {
744        triggered: bool,
745        trig_src: TriggerSource,
746        chan: &'a mut Channel<Id, ReadyFuture>,
747    }
748
749    impl<'a, Id: ChId> TransferFuture<'a, Id> {
750        pub(super) fn new(chan: &'a mut Channel<Id, ReadyFuture>, trig_src: TriggerSource) -> Self {
751            Self {
752                triggered: false,
753                trig_src,
754                chan,
755            }
756        }
757    }
758
759    impl<Id: ChId> Drop for TransferFuture<'_, Id> {
760        fn drop(&mut self) {
761            self.chan.stop();
762        }
763    }
764
765    impl<Id: ChId> core::future::Future for TransferFuture<'_, Id> {
766        type Output = ();
767
768        fn poll(
769            mut self: core::pin::Pin<&mut Self>,
770            cx: &mut core::task::Context<'_>,
771        ) -> core::task::Poll<Self::Output> {
772            use crate::dmac::waker::WAKERS;
773            use core::task::Poll;
774
775            self.chan._enable_private();
776
777            if !self.triggered && self.trig_src == TriggerSource::Disable {
778                self.triggered = true;
779                self.chan._trigger_private();
780            }
781
782            let flags_to_check = InterruptFlags::new().with_tcmpl(true).with_terr(true);
783
784            if self.chan.check_and_clear_interrupts(flags_to_check).tcmpl() {
785                return Poll::Ready(());
786            }
787
788            WAKERS[Id::USIZE].register(cx.waker());
789            self.chan.enable_interrupts(flags_to_check);
790
791            if self.chan.check_and_clear_interrupts(flags_to_check).tcmpl() {
792                self.chan.disable_interrupts(flags_to_check);
793
794                return Poll::Ready(());
795            }
796
797            Poll::Pending
798        }
799    }
800}
801
802/// Interrupt sources available to a DMA channel
803#[bitfield]
804#[repr(u8)]
805#[derive(Clone, Copy)]
806pub struct InterruptFlags {
807    /// Transfer error
808    pub terr: bool,
809    /// Transfer complete
810    pub tcmpl: bool,
811    /// Transfer suspended
812    pub susp: bool,
813    #[skip]
814    _reserved: B5,
815}
816
817impl Default for InterruptFlags {
818    fn default() -> Self {
819        Self::new()
820    }
821}
822
823/// Generate a [`DmacDescriptor`], and write it to the provided descriptor
824/// reference.
825///
826/// `next` is the address of the next descriptor (for linked transfers). If
827/// it is set to `0`, the transfer will terminate after this descriptor. For
828/// circular transfers, set `next` to the descriptor's own address.
829///
830/// # Safety
831///
832/// * This method may only be called on a channel which is not actively being
833///   used for transferring data.
834///
835/// * `next` must point to a valid [`DmacDescriptor`], with all the safety
836///   considerations that entails: the source and destination buffers must be
837///   valid, have compatible lengths, remain in scope for the entirety of the
838///   transfer, etc.
839///
840/// * The pointer in the `descaddr` field of `next`, along with the descriptor
841///   it points to, etc, must point to a valid [`DmacDescriptor`] memory
842///   location, or be null. They must not be circular (ie, points to itself).
843///   Any linked transfer must strictly be a read transaction (destination
844///   pointer is a byte buffer, source pointer is the SERCOM DATA register).
845#[inline]
846pub(crate) unsafe fn write_descriptor<Src: Buffer, Dst: Buffer<Beat = Src::Beat>>(
847    descriptor: &mut DmacDescriptor,
848    source: &mut Src,
849    destination: &mut Dst,
850    next: *mut DmacDescriptor,
851) {
852    let src_ptr = source.dma_ptr();
853    let src_inc = source.incrementing();
854    let src_len = source.buffer_len();
855
856    let dst_ptr = destination.dma_ptr();
857    let dst_inc = destination.incrementing();
858    let dst_len = destination.buffer_len();
859
860    let length = core::cmp::max(src_len, dst_len);
861
862    // Channel::xfer_complete() tests the channel enable bit, which indicates
863    // that a transfer has completed iff the blockact field in btctrl is not
864    // set to SUSPEND.  We implicitly leave blockact set to NOACT here; if
865    // that changes Channel::xfer_complete() may need to be modified.
866    let btctrl = sram::BlockTransferControl::new()
867        .with_srcinc(src_inc)
868        .with_dstinc(dst_inc)
869        .with_beatsize(Src::Beat::BEATSIZE)
870        .with_valid(true);
871
872    *descriptor = DmacDescriptor {
873        // Next descriptor address:  0x0 terminates the transaction (no linked list),
874        // any other address points to the next block descriptor
875        descaddr: next,
876        // Source address: address of the last beat transfer source in block
877        srcaddr: src_ptr as *mut _,
878        // Destination address: address of the last beat transfer destination in block
879        dstaddr: dst_ptr as *mut _,
880        // Block transfer count: number of beats in block transfer
881        btcnt: length as u16,
882        // Block transfer control: Datasheet  section 19.8.2.1 p.329
883        btctrl,
884    };
885}