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}