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}