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`](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        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    /// * You must ensure that the transfer is completed or stopped before
547    ///   returning the [`Channel`]. Doing otherwise breaks type safety, because
548    ///   a [`Ready`] channel would still be in the middle of a transfer.
549    /// * If the provided `linked_descriptor` is `Some` it must not be dropped
550    ///   until the transfer is completed or stopped.
551    /// * Additionnally, this function doesn't take `'static` buffers. Again,
552    ///   you must guarantee that the returned transfer has completed or has
553    ///   been stopped before giving up control of the underlying [`Channel`].
554    #[inline]
555    pub(crate) unsafe fn transfer_unchecked<S, D>(
556        &mut self,
557        source: &mut S,
558        dest: &mut D,
559        trig_src: TriggerSource,
560        trig_act: TriggerAction,
561        linked_descriptor: Option<&mut DmacDescriptor>,
562    ) where
563        S: Buffer,
564        D: Buffer<Beat = S::Beat>,
565    {
566        unsafe {
567            self.fill_descriptor(source, dest, false);
568
569            if let Some(next) = linked_descriptor {
570                self.link_next(next as *mut _);
571            }
572        }
573
574        self.configure_trigger(trig_src, trig_act);
575        self._enable_private();
576
577        if trig_src == TriggerSource::Disable {
578            self._trigger_private();
579        }
580    }
581}
582
583/// These methods may only be used on a `Busy` DMA channel
584impl<Id: ChId> Channel<Id, Busy> {
585    /// Issue a software trigger to the channel
586    #[inline]
587    pub(crate) fn software_trigger(&mut self) {
588        self._trigger_private();
589    }
590
591    /// Stop transfer on channel whether or not the transfer has completed, and
592    /// return the resources it holds.
593    ///
594    /// # Return
595    ///
596    /// A `Channel` with a `Ready` status, ready to be reused by a new
597    /// [`Transfer`](super::transfer::Transfer)
598    #[inline]
599    pub(crate) fn free(mut self) -> Channel<Id, Ready> {
600        self.stop();
601        self.change_status()
602    }
603
604    /// Restart transfer using previously-configured trigger source and action
605    #[inline]
606    pub(crate) fn restart(&mut self) {
607        self._enable_private();
608    }
609}
610
611impl<Id: ChId> From<Channel<Id, Ready>> for Channel<Id, Uninitialized> {
612    fn from(mut item: Channel<Id, Ready>) -> Self {
613        item._reset_private();
614        item.change_status()
615    }
616}
617
618#[cfg(feature = "async")]
619impl<Id: ChId> Channel<Id, ReadyFuture> {
620    /// Begin DMA transfer using `async` operation.
621    ///
622    /// If [`TriggerSource::Disable`] is used, a software
623    /// trigger will be issued to the DMA channel to launch the transfer. It
624    /// is therefore not necessary, in most cases, to manually issue a
625    /// software trigger to the channel.
626    ///
627    /// # Safety
628    ///
629    /// In `async` mode, a transfer does NOT require `'static` source and
630    /// destination buffers. This, in theory, makes
631    /// [`transfer_future`](Channel::transfer_future) an `unsafe` function,
632    /// although it is marked as safe for better ergonomics.
633    ///
634    /// This means that, as an user, you **must** ensure that the [`Future`]
635    /// returned by this function may never be forgotten through [`forget`] or
636    /// by wrapping it with a [`ManuallyDrop`].
637    ///
638    /// The returned future implements [`Drop`] and will automatically stop any
639    /// ongoing transfers to guarantee that the memory occupied by the
640    /// now-dropped buffers may not be corrupted by running transfers. This
641    /// also means that should you [`forget`] this [`Future`] after its
642    /// first [`poll`] call, the transfer will keep running, ruining the
643    /// now-reclaimed memory, as well as the rest of your day.
644    ///
645    /// * `await`ing is fine: the [`Future`] will run to completion.
646    /// * Dropping an incomplete transfer is also fine. Dropping can happen, for
647    ///   example, if the transfer doesn't complete before a timeout expires.
648    ///
649    /// [`forget`]: core::mem::forget
650    /// [`ManuallyDrop`]: core::mem::ManuallyDrop
651    /// [`Future`]: core::future::Future
652    /// [`poll`]: core::future::Future::poll
653    #[inline]
654    pub async fn transfer_future<S, D>(
655        &mut self,
656        mut source: S,
657        mut dest: D,
658        trig_src: TriggerSource,
659        trig_act: TriggerAction,
660    ) -> Result<(), super::Error>
661    where
662        S: super::Buffer,
663        D: super::Buffer<Beat = S::Beat>,
664    {
665        unsafe {
666            self.transfer_future_linked(&mut source, &mut dest, trig_src, trig_act, None)
667                .await
668        }
669    }
670
671    /// Begin an async transfer, without changing the channel's type to
672    /// [`Busy`].
673    ///
674    /// Also provides support for linked transfers via an optional `&mut
675    /// DmacDescriptor`.
676    ///
677    /// # Safety
678    ///
679    /// * This method does not check that the two provided buffers have
680    ///   compatible lengths. You must guarantee that:
681    ///   - Either `source` or `dest` has a buffer length of 1, or
682    ///   - Both buffers have the same length.
683    /// * You must ensure that the transfer is completed or stopped before
684    ///   returning the [`Channel`]. Doing otherwise breaks type safety, because
685    ///   a [`ReadyFuture`] channel would still be in the middle of a transfer.
686    /// * If the provided `linked_descriptor` is `Some` it must not be dropped
687    ///   until the transfer is completed or stopped.
688    /// * Additionnally, this function doesn't take `'static` buffers. Again,
689    ///   you must guarantee that the returned transfer has completed or has
690    ///   been stopped before giving up control of the underlying [`Channel`].
691    pub(crate) async unsafe fn transfer_future_linked<S, D>(
692        &mut self,
693        source: &mut S,
694        dest: &mut D,
695        trig_src: TriggerSource,
696        trig_act: TriggerAction,
697        linked_descriptor: Option<&mut DmacDescriptor>,
698    ) -> Result<(), super::Error>
699    where
700        S: super::Buffer,
701        D: super::Buffer<Beat = S::Beat>,
702    {
703        super::Transfer::<Self, super::transfer::BufferPair<S, D>>::check_buffer_pair(
704            source, dest,
705        )?;
706        unsafe {
707            self.fill_descriptor(source, dest, false);
708            if let Some(next) = linked_descriptor {
709                self.link_next(next as *mut _);
710            }
711        }
712
713        self.disable_interrupts(
714            InterruptFlags::new()
715                .with_susp(true)
716                .with_tcmpl(true)
717                .with_terr(true),
718        );
719
720        self.configure_trigger(trig_src, trig_act);
721
722        transfer_future::TransferFuture::new(self, trig_src).await;
723
724        // No need to defensively disable channel here; it's automatically stopped when
725        // TransferFuture is dropped. Even though `stop()` is implicitly called
726        // through TransferFuture::drop, it *absolutely* must be called before
727        // this function is returned, because it emits the compiler fence which ensures
728        // memory operations aren't reordered beyond the DMA transfer's bounds.
729
730        // TODO currently this will always return Ok(()) since we unconditionally clear
731        // ERROR
732        self.xfer_success()
733    }
734}
735
736#[cfg(feature = "async")]
737mod transfer_future {
738    use super::*;
739
740    /// [`Future`](core::future::Future) which starts, then waits on a DMA
741    /// transfer.
742    ///
743    /// This implementation is a standalone struct instead of using
744    /// [`poll_fn`](core::future::poll_fn), because we want to implement
745    /// [`Drop`] for the future returned by the
746    /// [`transfer_future`](super::Channel::transfer_future) method. This way we
747    /// can stop transfers when they are dropped, thus avoiding undefined
748    /// behaviour.
749    pub(super) struct TransferFuture<'a, Id: ChId> {
750        triggered: bool,
751        trig_src: TriggerSource,
752        chan: &'a mut Channel<Id, ReadyFuture>,
753    }
754
755    impl<'a, Id: ChId> TransferFuture<'a, Id> {
756        pub(super) fn new(chan: &'a mut Channel<Id, ReadyFuture>, trig_src: TriggerSource) -> Self {
757            Self {
758                triggered: false,
759                trig_src,
760                chan,
761            }
762        }
763    }
764
765    impl<Id: ChId> Drop for TransferFuture<'_, Id> {
766        fn drop(&mut self) {
767            self.chan.stop();
768        }
769    }
770
771    impl<Id: ChId> core::future::Future for TransferFuture<'_, Id> {
772        type Output = ();
773
774        fn poll(
775            mut self: core::pin::Pin<&mut Self>,
776            cx: &mut core::task::Context<'_>,
777        ) -> core::task::Poll<Self::Output> {
778            use crate::dmac::waker::WAKERS;
779            use core::task::Poll;
780
781            self.chan._enable_private();
782
783            if !self.triggered && self.trig_src == TriggerSource::Disable {
784                self.triggered = true;
785                self.chan._trigger_private();
786            }
787
788            let flags_to_check = InterruptFlags::new().with_tcmpl(true).with_terr(true);
789
790            if self.chan.check_and_clear_interrupts(flags_to_check).tcmpl() {
791                return Poll::Ready(());
792            }
793
794            WAKERS[Id::USIZE].register(cx.waker());
795            self.chan.enable_interrupts(flags_to_check);
796
797            if self.chan.check_and_clear_interrupts(flags_to_check).tcmpl() {
798                self.chan.disable_interrupts(flags_to_check);
799
800                return Poll::Ready(());
801            }
802
803            Poll::Pending
804        }
805    }
806}
807
808/// Interrupt sources available to a DMA channel
809#[bitfield]
810#[repr(u8)]
811#[derive(Clone, Copy)]
812pub struct InterruptFlags {
813    /// Transfer error
814    pub terr: bool,
815    /// Transfer complete
816    pub tcmpl: bool,
817    /// Transfer suspended
818    pub susp: bool,
819    #[skip]
820    _reserved: B5,
821}
822
823impl Default for InterruptFlags {
824    fn default() -> Self {
825        Self::new()
826    }
827}
828
829/// Generate a [`DmacDescriptor`], and write it to the provided descriptor
830/// reference.
831///
832/// `next` is the address of the next descriptor (for linked transfers). If
833/// it is set to `0`, the transfer will terminate after this descriptor. For
834/// circular transfers, set `next` to the descriptor's own address.
835///
836/// # Safety
837///
838/// * This method may only be called on a channel which is not actively being
839///   used for transferring data.
840///
841/// * `next` must point to a valid [`DmacDescriptor`], with all the safety
842///   considerations that entails: the source and destination buffers must be
843///   valid, have compatible lengths, remain in scope for the entirety of the
844///   transfer, etc.
845///
846/// * The pointer in the `descaddr` field of `next`, along with the descriptor
847///   it points to, etc, must point to a valid [`DmacDescriptor`] memory
848///   location, or be null. They must not be circular (ie, points to itself).
849///   Any linked transfer must strictly be a read transaction (destination
850///   pointer is a byte buffer, source pointer is the SERCOM DATA register).
851#[inline]
852pub(crate) unsafe fn write_descriptor<Src: Buffer, Dst: Buffer<Beat = Src::Beat>>(
853    descriptor: &mut DmacDescriptor,
854    source: &mut Src,
855    destination: &mut Dst,
856    next: *mut DmacDescriptor,
857) {
858    let src_ptr = source.dma_ptr();
859    let src_inc = source.incrementing();
860    let src_len = source.buffer_len();
861
862    let dst_ptr = destination.dma_ptr();
863    let dst_inc = destination.incrementing();
864    let dst_len = destination.buffer_len();
865
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}