Skip to main content

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    Beat, Buffer, Error,
43    dma_controller::{ChId, PriorityLevel, TriggerAction, TriggerSource},
44    sram::{self, DmacDescriptor},
45    transfer::{BufferPair, Transfer},
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`].
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        unsafe {
355            write_descriptor(descriptor, source, destination, descaddr);
356        }
357    }
358
359    /// Add a linked descriptor after the first descriptor in the transfer.
360    ///
361    /// # Safety
362    ///
363    /// * This method may only be called on a channel which is not actively
364    ///   being used for transferring data.
365    ///
366    /// * `next` must point to a valid [`DmacDescriptor`], with all the safety
367    ///   considerations that entails: the source and destination buffers must
368    ///   be valid, have compatible lengths, remain in scope for the entirety of
369    ///   the transfer, etc.
370    ///
371    /// * The pointer in the `descaddr` field of `next`, along with the
372    ///   descriptor it points to, etc, must point to a valid [`DmacDescriptor`]
373    ///   memory location, or be null. They must not be circular (ie, points to
374    ///   itself). Any linked transfer must strictly be a read transaction
375    ///   (destination pointer is a byte buffer, source pointer is the SERCOM
376    ///   DATA register).
377    pub(super) unsafe fn link_next(&mut self, next: *mut DmacDescriptor) {
378        self.descriptor_mut().descaddr = next;
379    }
380}
381
382impl<Id, R> Channel<Id, R>
383where
384    Id: ChId,
385    R: ReadyChannel,
386{
387    /// Issue a software reset to the channel. This will return the channel to
388    /// its startup state
389    #[inline]
390    pub fn reset(mut self) -> Channel<Id, R::Uninitialized> {
391        self._reset_private();
392        self.change_status()
393    }
394
395    /// Set the FIFO threshold length. The channel will wait until it has
396    /// received the selected number of Beats before triggering the Burst
397    /// transfer, reducing the DMA transfer latency.
398    #[hal_cfg("dmac-d5x")]
399    #[inline]
400    pub fn fifo_threshold(&mut self, threshold: FifoThreshold) {
401        self.regs
402            .chctrla
403            .modify(|_, w| w.threshold().variant(threshold));
404    }
405
406    #[cfg(doc)]
407    #[hal_cfg(not("dmac-d5x"))]
408    /// This method is not present with the selected feature set, defined for
409    /// documentation only
410    pub fn fifo_threshold(&mut self) {
411        unimplemented!()
412    }
413
414    /// Set burst length for the channel, in number of beats. A burst transfer
415    /// is an atomic, uninterruptible operation.
416    #[hal_cfg("dmac-d5x")]
417    #[inline]
418    pub fn burst_length(&mut self, burst_length: BurstLength) {
419        self.regs
420            .chctrla
421            .modify(|_, w| w.burstlen().variant(burst_length));
422    }
423
424    #[cfg(doc)]
425    #[hal_cfg(not("dmac-d5x"))]
426    /// This method is not present with the selected feature set, defined for
427    /// documentation only
428    pub fn burst_length(&mut self) {
429        unimplemented!()
430    }
431
432    /// Start the transfer.
433    ///
434    /// # Safety
435    ///
436    /// This function is unsafe because it starts the transfer without changing
437    /// the channel status to [`Busy`]. A [`Ready`] channel which is actively
438    /// transferring should NEVER be leaked.
439    #[inline]
440    #[hal_macro_helper]
441    pub(super) unsafe fn _start_private(
442        &mut self,
443        trig_src: TriggerSource,
444        trig_act: TriggerAction,
445    ) {
446        // Configure the trigger source and trigger action
447        self.configure_trigger(trig_src, trig_act);
448        self._enable_private();
449
450        // If trigger source is DISABLE, manually trigger transfer
451        if trig_src == TriggerSource::Disable {
452            self._trigger_private();
453        }
454    }
455
456    #[inline]
457    #[hal_macro_helper]
458    pub(super) fn configure_trigger(&mut self, trig_src: TriggerSource, trig_act: TriggerAction) {
459        // Configure the trigger source and trigger action
460        #[hal_cfg(any("dmac-d11", "dmac-d21"))]
461        self.regs.chctrlb.modify(|_, w| {
462            w.trigsrc().variant(trig_src);
463            w.trigact().variant(trig_act)
464        });
465
466        #[hal_cfg("dmac-d5x")]
467        self.regs.chctrla.modify(|_, w| {
468            w.trigsrc().variant(trig_src);
469            w.trigact().variant(trig_act)
470        });
471    }
472}
473
474impl<Id: ChId> Channel<Id, Ready> {
475    /// Start transfer on channel using the specified trigger source.
476    ///
477    /// # Return
478    ///
479    /// A `Channel` with a `Busy` status.
480    #[inline]
481    pub(crate) fn start(
482        mut self,
483        trig_src: TriggerSource,
484        trig_act: TriggerAction,
485    ) -> Channel<Id, Busy> {
486        unsafe {
487            self._start_private(trig_src, trig_act);
488        }
489        self.change_status()
490    }
491
492    /// Begin a [`Transfer`], without changing the channel's type to [`Busy`].
493    ///
494    /// This method provides an additional safety guarantee over
495    /// [`Self::transfer_unchecked`]; it checks that the buffer lengths are
496    /// valid before attempting to start the transfer.
497    ///
498    /// Also provides support for linked transfers via an optional `&mut
499    /// DmacDescriptor`.
500    ///
501    /// This function guarantees that it will never return [`Err`] if the
502    /// transfer has been started.
503    ///
504    /// # Safety
505    ///
506    /// * You must ensure that the transfer is completed or stopped before
507    ///   returning the [`Channel`]. Doing otherwise breaks type safety, because
508    ///   a [`Ready`] channel would still be in the middle of a transfer.
509    /// * If the provided `linked_descriptor` is `Some` it must not be dropped
510    ///   until the transfer is completed or stopped.
511    /// * Additionnally, this function doesn't take `'static` buffers. Again,
512    ///   you must guarantee that the returned transfer has completed or has
513    ///   been stopped before giving up control of the underlying [`Channel`].
514    #[inline]
515    #[allow(dead_code)]
516    pub(crate) unsafe fn transfer<S, D>(
517        &mut self,
518        source: &mut S,
519        dest: &mut D,
520        trig_src: TriggerSource,
521        trig_act: TriggerAction,
522        linked_descriptor: Option<&mut DmacDescriptor>,
523    ) -> Result<(), Error>
524    where
525        S: Buffer,
526        D: Buffer<Beat = S::Beat>,
527    {
528        Transfer::<Self, BufferPair<S, D>>::check_buffer_pair(source, dest)?;
529        unsafe {
530            self.transfer_unchecked(source, dest, trig_src, trig_act, linked_descriptor);
531        }
532        Ok(())
533    }
534
535    /// Begin a transfer, without changing the channel's type to [`Busy`].
536    ///
537    /// Also provides support for linked transfers via an optional `&mut
538    /// DmacDescriptor`.
539    ///
540    /// # Safety
541    ///
542    /// * This method does not check that the two provided buffers have
543    ///   compatible lengths. You must guarantee that:
544    ///   - Either `source` or `dest` has a buffer length of 1, or
545    ///   - Both buffers have the same length.
546    /// * The source and destination buffers must have a length smaller or equal
547    ///   to `u16::MAX`.
548    /// * You must ensure that the transfer is completed or stopped before
549    ///   returning the [`Channel`]. Doing otherwise breaks type safety, because
550    ///   a [`Ready`] channel would still be in the middle of a transfer.
551    /// * If the provided `linked_descriptor` is `Some` it must not be dropped
552    ///   until the transfer is completed or stopped.
553    /// * Additionnally, this function doesn't take `'static` buffers. Again,
554    ///   you must guarantee that the returned transfer has completed or has
555    ///   been stopped before giving up control of the underlying [`Channel`].
556    #[inline]
557    pub(crate) unsafe fn transfer_unchecked<S, D>(
558        &mut self,
559        source: &mut S,
560        dest: &mut D,
561        trig_src: TriggerSource,
562        trig_act: TriggerAction,
563        linked_descriptor: Option<&mut DmacDescriptor>,
564    ) where
565        S: Buffer,
566        D: Buffer<Beat = S::Beat>,
567    {
568        unsafe {
569            self.fill_descriptor(source, dest, false);
570
571            if let Some(next) = linked_descriptor {
572                self.link_next(next as *mut _);
573            }
574        }
575
576        self.configure_trigger(trig_src, trig_act);
577        self._enable_private();
578
579        if trig_src == TriggerSource::Disable {
580            self._trigger_private();
581        }
582    }
583}
584
585/// These methods may only be used on a `Busy` DMA channel
586impl<Id: ChId> Channel<Id, Busy> {
587    /// Issue a software trigger to the channel
588    #[inline]
589    pub(crate) fn software_trigger(&mut self) {
590        self._trigger_private();
591    }
592
593    /// Stop transfer on channel whether or not the transfer has completed, and
594    /// return the resources it holds.
595    ///
596    /// # Return
597    ///
598    /// A `Channel` with a `Ready` status, ready to be reused by a new
599    /// [`Transfer`](super::transfer::Transfer)
600    #[inline]
601    pub(crate) fn free(mut self) -> Channel<Id, Ready> {
602        self.stop();
603        self.change_status()
604    }
605
606    /// Restart transfer using previously-configured trigger source and action
607    #[inline]
608    pub(crate) fn restart(&mut self) {
609        self._enable_private();
610    }
611}
612
613impl<Id: ChId> From<Channel<Id, Ready>> for Channel<Id, Uninitialized> {
614    fn from(mut item: Channel<Id, Ready>) -> Self {
615        item._reset_private();
616        item.change_status()
617    }
618}
619
620#[cfg(feature = "async")]
621impl<Id: ChId> Channel<Id, ReadyFuture> {
622    /// Begin DMA transfer using `async` operation.
623    ///
624    /// If [`TriggerSource::Disable`] is used, a software
625    /// trigger will be issued to the DMA channel to launch the transfer. It
626    /// is therefore not necessary, in most cases, to manually issue a
627    /// software trigger to the channel.
628    ///
629    /// # Safety
630    ///
631    /// In `async` mode, a transfer does NOT require `'static` source and
632    /// destination buffers. This, in theory, makes
633    /// [`transfer_future`](Channel::transfer_future) an `unsafe` function,
634    /// although it is marked as safe for better ergonomics.
635    ///
636    /// This means that, as an user, you **must** ensure that the [`Future`]
637    /// returned by this function may never be forgotten through [`forget`] or
638    /// by wrapping it with a [`ManuallyDrop`].
639    ///
640    /// The returned future implements [`Drop`] and will automatically stop any
641    /// ongoing transfers to guarantee that the memory occupied by the
642    /// now-dropped buffers may not be corrupted by running transfers. This
643    /// also means that should you [`forget`] this [`Future`] after its
644    /// first [`poll`] call, the transfer will keep running, ruining the
645    /// now-reclaimed memory, as well as the rest of your day.
646    ///
647    /// * `await`ing is fine: the [`Future`] will run to completion.
648    /// * Dropping an incomplete transfer is also fine. Dropping can happen, for
649    ///   example, if the transfer doesn't complete before a timeout expires.
650    ///
651    /// [`forget`]: core::mem::forget
652    /// [`ManuallyDrop`]: core::mem::ManuallyDrop
653    /// [`Future`]: core::future::Future
654    /// [`poll`]: core::future::Future::poll
655    #[inline]
656    pub async fn transfer_future<S, D>(
657        &mut self,
658        mut source: S,
659        mut dest: D,
660        trig_src: TriggerSource,
661        trig_act: TriggerAction,
662    ) -> Result<(), super::Error>
663    where
664        S: super::Buffer,
665        D: super::Buffer<Beat = S::Beat>,
666    {
667        unsafe {
668            self.transfer_future_linked(&mut source, &mut dest, trig_src, trig_act, None)
669                .await
670        }
671    }
672
673    /// Begin an async transfer, without changing the channel's type to
674    /// [`Busy`].
675    ///
676    /// Also provides support for linked transfers via an optional `&mut
677    /// DmacDescriptor`.
678    ///
679    /// # Safety
680    ///
681    /// * You must ensure that the transfer is completed or stopped before
682    ///   returning the [`Channel`]. Doing otherwise breaks type safety, because
683    ///   a [`ReadyFuture`] channel would still be in the middle of a transfer.
684    /// * If the provided `linked_descriptor` is `Some` it must not be dropped
685    ///   until the transfer is completed or stopped.
686    /// * Additionnally, this function doesn't take `'static` buffers. Again,
687    ///   you must guarantee that the returned transfer has completed or has
688    ///   been stopped before giving up control of the underlying [`Channel`].
689    pub(crate) async unsafe fn transfer_future_linked<S, D>(
690        &mut self,
691        source: &mut S,
692        dest: &mut D,
693        trig_src: TriggerSource,
694        trig_act: TriggerAction,
695        linked_descriptor: Option<&mut DmacDescriptor>,
696    ) -> Result<(), super::Error>
697    where
698        S: super::Buffer,
699        D: super::Buffer<Beat = S::Beat>,
700    {
701        super::Transfer::<Self, super::transfer::BufferPair<S, D>>::check_buffer_pair(
702            source, dest,
703        )?;
704        unsafe {
705            self.fill_descriptor(source, dest, false);
706            if let Some(next) = linked_descriptor {
707                self.link_next(next as *mut _);
708            }
709        }
710
711        self.disable_interrupts(
712            InterruptFlags::new()
713                .with_susp(true)
714                .with_tcmpl(true)
715                .with_terr(true),
716        );
717
718        self.configure_trigger(trig_src, trig_act);
719
720        transfer_future::TransferFuture::new(self, trig_src).await
721
722        // No need to defensively disable channel here; it's automatically
723        // stopped when TransferFuture is dropped. Even though `stop()`
724        // is implicitly called through TransferFuture::drop, it
725        // *absolutely* must be called before this function is returned,
726        // because it emits the compiler fence which ensures memory operations
727        // aren't reordered beyond the DMA transfer's bounds.
728    }
729}
730
731#[cfg(feature = "async")]
732mod transfer_future {
733    use super::*;
734
735    /// [`Future`](core::future::Future) which starts, then waits on a DMA
736    /// transfer.
737    ///
738    /// This implementation is a standalone struct instead of using
739    /// [`poll_fn`](core::future::poll_fn), because we want to implement
740    /// [`Drop`] for the future returned by the
741    /// [`transfer_future`](super::Channel::transfer_future) method. This way we
742    /// can stop transfers when they are dropped, thus avoiding undefined
743    /// behaviour.
744    pub(super) struct TransferFuture<'a, Id: ChId> {
745        triggered: bool,
746        trig_src: TriggerSource,
747        chan: &'a mut Channel<Id, ReadyFuture>,
748    }
749
750    impl<'a, Id: ChId> TransferFuture<'a, Id> {
751        pub(super) fn new(chan: &'a mut Channel<Id, ReadyFuture>, trig_src: TriggerSource) -> Self {
752            Self {
753                triggered: false,
754                trig_src,
755                chan,
756            }
757        }
758    }
759
760    impl<Id: ChId> Drop for TransferFuture<'_, Id> {
761        fn drop(&mut self) {
762            self.chan.stop();
763        }
764    }
765
766    impl<Id: ChId> core::future::Future for TransferFuture<'_, Id> {
767        type Output = Result<(), super::Error>;
768
769        fn poll(
770            mut self: core::pin::Pin<&mut Self>,
771            cx: &mut core::task::Context<'_>,
772        ) -> core::task::Poll<Self::Output> {
773            use crate::dmac::waker::WAKERS;
774            use core::task::Poll;
775
776            let flags_to_check = InterruptFlags::new().with_tcmpl(true).with_terr(true);
777            let triggered_flags = self.chan.check_and_clear_interrupts(flags_to_check);
778
779            if triggered_flags.tcmpl() {
780                Poll::Ready(Ok(()))
781            } else if triggered_flags.terr() {
782                Poll::Ready(Err(super::Error::TransferError))
783            } else {
784                WAKERS[Id::USIZE].register(cx.waker());
785                self.chan.enable_interrupts(flags_to_check);
786                self.chan._enable_private();
787
788                if !self.triggered && self.trig_src == TriggerSource::Disable {
789                    self.triggered = true;
790                    self.chan._trigger_private();
791                }
792
793                Poll::Pending
794            }
795        }
796    }
797}
798
799/// Interrupt sources available to a DMA channel
800#[bitfield]
801#[repr(u8)]
802#[derive(Clone, Copy)]
803pub struct InterruptFlags {
804    /// Transfer error
805    pub terr: bool,
806    /// Transfer complete
807    pub tcmpl: bool,
808    /// Transfer suspended
809    pub susp: bool,
810    #[skip]
811    _reserved: B5,
812}
813
814impl Default for InterruptFlags {
815    fn default() -> Self {
816        Self::new()
817    }
818}
819
820/// Generate a [`DmacDescriptor`], and write it to the provided descriptor
821/// reference.
822///
823/// `next` is the address of the next descriptor (for linked transfers). If
824/// it is set to `0`, the transfer will terminate after this descriptor. For
825/// circular transfers, set `next` to the descriptor's own address.
826///
827/// # Safety
828///
829/// * This method may only be called on a channel which is not actively being
830///   used for transferring data.
831///
832/// * `next` must point to a valid [`DmacDescriptor`], with all the safety
833///   considerations that entails: the source and destination buffers must be
834///   valid, have compatible lengths, remain in scope for the entirety of the
835///   transfer, etc.
836///
837/// * The pointer in the `descaddr` field of `next`, along with the descriptor
838///   it points to, etc, must point to a valid [`DmacDescriptor`] memory
839///   location, or be null. They must not be circular (ie, points to itself).
840///   Any linked transfer must strictly be a read transaction (destination
841///   pointer is a byte buffer, source pointer is the SERCOM DATA register).
842///
843/// * The length of both the source and destination buffers must be smaller or
844///   equal to `u16::MAX`.
845///
846/// * Either:
847///      - `source` or `dest` has a buffer length of 1, or
848///      - Both buffers have the same length.
849#[inline]
850pub(crate) unsafe fn write_descriptor<Src: Buffer, Dst: Buffer<Beat = Src::Beat>>(
851    descriptor: &mut DmacDescriptor,
852    source: &mut Src,
853    destination: &mut Dst,
854    next: *mut DmacDescriptor,
855) {
856    let src_ptr = source.dma_ptr();
857    let src_inc = source.incrementing();
858    let src_len = source.buffer_len();
859
860    let dst_ptr = destination.dma_ptr();
861    let dst_inc = destination.incrementing();
862    let dst_len = destination.buffer_len();
863
864    // This is sufficient since buffers of unequal lengths breaks the safety
865    // contract if neither buffer has a length of 1.
866    let length = core::cmp::max(src_len, dst_len);
867
868    // Channel::xfer_complete() tests the channel enable bit, which indicates
869    // that a transfer has completed iff the blockact field in btctrl is not
870    // set to SUSPEND.  We implicitly leave blockact set to NOACT here; if
871    // that changes Channel::xfer_complete() may need to be modified.
872    let btctrl = sram::BlockTransferControl::new()
873        .with_srcinc(src_inc)
874        .with_dstinc(dst_inc)
875        .with_beatsize(Src::Beat::BEATSIZE)
876        .with_valid(true);
877
878    *descriptor = DmacDescriptor {
879        // Next descriptor address:  0x0 terminates the transaction (no linked list),
880        // any other address points to the next block descriptor
881        descaddr: next,
882        // Source address: address of the last beat transfer source in block
883        srcaddr: src_ptr as *mut _,
884        // Destination address: address of the last beat transfer destination in block
885        dstaddr: dst_ptr as *mut _,
886        // Block transfer count: number of beats in block transfer
887        btcnt: length as u16,
888        // Block transfer control: Datasheet  section 19.8.2.1 p.329
889        btctrl,
890    };
891}