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