atsamd_hal/sercom/
dma.rs

1//! Use the DMA Controller to perform transfers using the SERCOM peripheral
2//!
3//! See the [`mod@uart`], [`mod@i2c`] and [`mod@spi`] modules for the
4//! corresponding DMA transfer implementations.
5
6use core::{marker::PhantomData, ops::Range};
7
8use atsamd_hal_macros::hal_macro_helper;
9
10use crate::{
11    dmac::{
12        self,
13        channel::{AnyChannel, Busy, Channel, InterruptFlags, Ready},
14        sram::DmacDescriptor,
15        transfer::BufferPair,
16        Beat, Buffer, Transfer, TriggerAction,
17    },
18    sercom::{
19        i2c::{self, I2c},
20        spi::{self, Spi},
21        uart::{self, Uart},
22        Sercom,
23    },
24};
25
26/// Wrapper type over an `&[T]` that can be used as a source buffer for DMA
27/// transfers. This is an implementation detail to make SERCOM-DMA
28/// transfers work. Should not be used outside of this crate.
29///
30/// # Safety
31///
32/// [`SharedSliceBuffer`]s should only ever be used as **source** buffers for
33/// DMA transfers, and never as destination buffers.
34#[doc(hidden)]
35pub(crate) struct SharedSliceBuffer<'a, T: Beat> {
36    ptrs: Range<*mut T>,
37    _lifetime: PhantomData<&'a T>,
38}
39
40impl<'a, T: Beat> SharedSliceBuffer<'a, T> {
41    #[inline]
42    pub(in super::super) fn from_slice(slice: &'a [T]) -> Self {
43        unsafe { Self::from_slice_unchecked(slice) }
44    }
45
46    #[inline]
47    pub(in super::super) unsafe fn from_slice_unchecked(slice: &[T]) -> Self {
48        let ptrs = slice.as_ptr_range();
49
50        let ptrs = Range {
51            start: ptrs.start.cast_mut(),
52            end: ptrs.end.cast_mut(),
53        };
54
55        Self {
56            ptrs,
57            _lifetime: PhantomData,
58        }
59    }
60}
61
62unsafe impl<T: Beat> Buffer for SharedSliceBuffer<'_, T> {
63    type Beat = T;
64    #[inline]
65    fn dma_ptr(&mut self) -> *mut Self::Beat {
66        if self.incrementing() {
67            self.ptrs.end
68        } else {
69            self.ptrs.start
70        }
71    }
72
73    #[inline]
74    fn incrementing(&self) -> bool {
75        self.buffer_len() > 1
76    }
77
78    #[inline]
79    fn buffer_len(&self) -> usize {
80        self.ptrs.end as usize - self.ptrs.start as usize
81    }
82}
83
84/// Sink/source buffer to use for unidirectional SPI-DMA transfers.
85///
86/// When reading/writing from a [`Duplex`] [`Spi`] with DMA enabled,
87/// we must always read and write the same number of words, regardless of
88/// whether we care about the result (ie, for write, we discard the read
89/// words, whereas for read, we must send a no-op word).
90///
91/// This [`Buffer`] implementation provides a source/sink for a single word,
92/// but with a variable length.
93pub(super) struct SinkSourceBuffer<'a, T: Beat> {
94    word: &'a mut T,
95    length: usize,
96}
97
98impl<'a, T: Beat> SinkSourceBuffer<'a, T> {
99    pub(super) fn new(word: &'a mut T, length: usize) -> Self {
100        Self { word, length }
101    }
102}
103unsafe impl<T: Beat> Buffer for SinkSourceBuffer<'_, T> {
104    type Beat = T;
105    #[inline]
106    fn incrementing(&self) -> bool {
107        false
108    }
109
110    #[inline]
111    fn buffer_len(&self) -> usize {
112        self.length
113    }
114
115    #[inline]
116    fn dma_ptr(&mut self) -> *mut Self::Beat {
117        self.word as *mut _
118    }
119}
120/// Wrapper type over Sercom instances to get around lifetime issues when using
121/// one as a DMA source/destination buffer. This is an implementation detail to
122/// make SERCOM-DMA transfers work.
123#[doc(hidden)]
124#[derive(Clone)]
125pub(crate) struct SercomPtr<T: Beat>(pub(in super::super) *mut T);
126
127unsafe impl<T: Beat> Buffer for SercomPtr<T> {
128    type Beat = T;
129
130    #[inline]
131    fn dma_ptr(&mut self) -> *mut Self::Beat {
132        self.0
133    }
134
135    #[inline]
136    fn incrementing(&self) -> bool {
137        false
138    }
139
140    #[inline]
141    fn buffer_len(&self) -> usize {
142        1
143    }
144}
145
146//=============================================================================
147// I2C DMA transfers
148//=============================================================================
149
150/// Token type representing an [`I2c`] for which the bus is
151/// ready to start a transaction.
152///
153/// For use with [`send_with_dma`](super::i2c::I2c::send_with_dma) and
154/// [`receive_with_dma`](super::i2c::I2c::send_with_dma).
155#[deprecated(
156    since = "0.19.0",
157    note = "Use `I2c::with_dma_channel` instead. You will have access to DMA-enabled `embedded-hal` implementations."
158)]
159pub struct I2cBusReady;
160
161unsafe impl<C: i2c::AnyConfig> Buffer for I2c<C> {
162    type Beat = i2c::Word;
163
164    #[inline]
165    fn dma_ptr(&mut self) -> *mut Self::Beat {
166        self.data_ptr()
167    }
168
169    #[inline]
170    fn incrementing(&self) -> bool {
171        false
172    }
173
174    #[inline]
175    fn buffer_len(&self) -> usize {
176        1
177    }
178}
179
180impl<C: i2c::AnyConfig> I2c<C> {
181    /// Initialize the bus to start receiving with DMA.
182    ///
183    /// In reality, this function only checks whether or not the I2C bus is
184    /// ready to accept a new transaction. A call to [`init_dma_transfer`]
185    /// should immediately be followed by a call to [`send_with_dma`] or
186    /// [`receive_with_dma`].
187    ///
188    /// ```no_run
189    /// # fn init_transfer<A: i2c::AnyConfig, C: AnyChannel<dmac::Ready>>(i2c: I2c<A>, chan0: C, buf_src: &'static mut [u8]){
190    /// // Assume `i2c` is a fully configured `I2c`, and `chan0` a fully configured `dmac::Channel`.
191    /// let token = i2c.init_dma_transfer()?;
192    /// i2c.send_with_dma(ADDRESS, token, buf_src, chan0);
193    /// # }
194    /// ```
195    ///
196    /// [`init_dma_transfer`]: super::i2c::I2c::init_dma_transfer
197    /// [`send_with_dma`]: super::i2c::I2c::send_with_dma
198    /// [`receive_with_dma`]: super::i2c::I2c::receive_with_dma
199    #[deprecated(
200        since = "0.19.0",
201        note = "Use `I2c::with_dma_channel` instead. You will have access to DMA-enabled `embedded-hal` implementations."
202    )]
203    #[allow(deprecated)]
204    pub fn init_dma_transfer(&mut self) -> Result<I2cBusReady, super::i2c::Error> {
205        self.check_bus_status()?;
206        Ok(I2cBusReady)
207    }
208
209    /// Transform an [`I2c`] into a DMA [`Transfer`]) and
210    /// start receiving into the provided buffer. The buffer length must be 255
211    /// bytes or shorter.
212    ///
213    /// It is recommended that you check for errors after the transfer is
214    /// complete by calling [`read_status`](I2c::read_status).
215    #[deprecated(
216        since = "0.19.0",
217        note = "Use `I2c::with_dma_channel` instead. You will have access to DMA-enabled `embedded-hal` implementations."
218    )]
219    #[allow(deprecated)]
220    #[hal_macro_helper]
221    pub fn receive_with_dma<Ch, B>(
222        self,
223        address: u8,
224        _ready_token: I2cBusReady,
225        buf: B,
226        mut channel: Ch,
227    ) -> Transfer<Channel<Ch::Id, Busy>, BufferPair<Self, B>>
228    where
229        Ch: AnyChannel<Status = Ready>,
230        B: Buffer<Beat = i2c::Word> + 'static,
231    {
232        let len = buf.buffer_len();
233        assert!(len > 0 && len <= 255);
234
235        channel
236            .as_mut()
237            .enable_interrupts(InterruptFlags::new().with_tcmpl(true));
238
239        #[hal_cfg("sercom0-d5x")]
240        let trigger_action = TriggerAction::Burst;
241
242        #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
243        let trigger_action = TriggerAction::Beat;
244
245        // SAFETY: This is safe because the of the `'static` bound check
246        // for `B`, and the fact that the buffer length of an `I2c` is always 1.
247        let xfer = unsafe { dmac::Transfer::new_unchecked(channel, self, buf, false) };
248        let mut xfer = xfer.begin(C::Sercom::DMA_RX_TRIGGER, trigger_action);
249
250        // SAFETY: we borrow the source from under a `Busy` transfer. While the type
251        // system believes the transfer is running, we haven't enabled it in the
252        // I2C peripheral yet, and so a trigger won't happen until we call
253        // `start_dma_read`.
254        unsafe { xfer.borrow_source().start_dma_read(address, len as u8) };
255        xfer
256    }
257
258    /// Transform an [`I2c`] into a DMA [`Transfer`]) and
259    /// start sending the provided buffer. The buffer length must be 255 bytes
260    /// or shorter.
261    ///
262    /// It is recommended that you check for errors after the transfer is
263    /// complete by calling [`read_status`](I2c::read_status).
264    #[inline]
265    #[hal_macro_helper]
266    #[deprecated(
267        since = "0.19.0",
268        note = "Use `I2c::with_dma_chahnnel` instead. You will have access to DMA-enabled `embedded-hal` implementations."
269    )]
270    #[allow(deprecated)]
271    pub fn send_with_dma<Ch, B>(
272        self,
273        address: u8,
274        _ready_token: I2cBusReady,
275        buf: B,
276        mut channel: Ch,
277    ) -> Transfer<Channel<Ch::Id, Busy>, BufferPair<B, Self>>
278    where
279        Ch: AnyChannel<Status = Ready>,
280        B: Buffer<Beat = i2c::Word> + 'static,
281    {
282        let len = buf.buffer_len();
283        assert!(len > 0 && len <= 255);
284
285        channel
286            .as_mut()
287            .enable_interrupts(InterruptFlags::new().with_tcmpl(true));
288
289        #[hal_cfg("sercom0-d5x")]
290        let trigger_action = TriggerAction::Burst;
291
292        #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
293        let trigger_action = TriggerAction::Beat;
294
295        // SAFETY: This is safe because the of the `'static` bound check
296        // for `B`, and the fact that the buffer length of an `I2c` is always 1.
297        let xfer = unsafe { dmac::Transfer::new_unchecked(channel, buf, self, false) };
298        let mut xfer = xfer.begin(C::Sercom::DMA_TX_TRIGGER, trigger_action);
299
300        // SAFETY: we borrow the source from under a `Busy` transfer. While the type
301        // system believes the transfer is running, we haven't enabled it in the
302        // I2C peripheral yet, and so a trigger won't happen until we call
303        // `start_dma_write`.
304        unsafe {
305            xfer.borrow_destination()
306                .start_dma_write(address, len as u8)
307        };
308        xfer
309    }
310}
311
312//=============================================================================
313// UART DMA transfers
314//=============================================================================
315unsafe impl<C, D> Buffer for Uart<C, D>
316where
317    C: uart::ValidConfig,
318    C::Word: Beat,
319    D: uart::Capability,
320{
321    type Beat = C::Word;
322
323    #[inline]
324    fn dma_ptr(&mut self) -> *mut Self::Beat {
325        self.data_ptr()
326    }
327
328    #[inline]
329    fn incrementing(&self) -> bool {
330        false
331    }
332
333    #[inline]
334    fn buffer_len(&self) -> usize {
335        1
336    }
337}
338
339impl<C, D> Uart<C, D>
340where
341    Self: Buffer<Beat = C::Word>,
342    C: uart::ValidConfig,
343    D: uart::Receive,
344{
345    /// Transform an [`Uart`] into a DMA [`Transfer`]) and start reveiving into
346    /// the provided buffer.
347    ///
348    /// In order to be (safely) non-blocking, his method has to take a `'static`
349    /// buffer. If you'd rather use DMA with the blocking
350    /// [`embedded_io::Read`](crate::embedded_io::Read) trait, and avoid having
351    /// to use static buffers,
352    /// use[`Uart::with_rx_channel`](Self::with_tx_channel) instead.
353    #[inline]
354    #[hal_macro_helper]
355    pub fn receive_with_dma<Ch, B>(
356        self,
357        buf: B,
358        mut channel: Ch,
359    ) -> Transfer<Channel<Ch::Id, Busy>, BufferPair<Self, B>>
360    where
361        Ch: AnyChannel<Status = Ready>,
362        B: Buffer<Beat = C::Word> + 'static,
363    {
364        channel
365            .as_mut()
366            .enable_interrupts(InterruptFlags::new().with_tcmpl(true));
367
368        #[hal_cfg("sercom0-d5x")]
369        let trigger_action = TriggerAction::Burst;
370
371        #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
372        let trigger_action = TriggerAction::Beat;
373
374        // SAFETY: This is safe because the of the `'static` bound check
375        // for `B`, and the fact that the buffer length of an `Uart` is always 1.
376        let xfer = unsafe { dmac::Transfer::new_unchecked(channel, self, buf, false) };
377        xfer.begin(C::Sercom::DMA_RX_TRIGGER, trigger_action)
378    }
379}
380
381impl<C, D> Uart<C, D>
382where
383    Self: Buffer<Beat = C::Word>,
384    C: uart::ValidConfig,
385    D: uart::Transmit,
386{
387    /// Transform an [`Uart`] into a DMA [`Transfer`]) and start sending the
388    /// provided buffer.
389    ///
390    /// In order to be (safely) non-blocking, his method takes a `'static`
391    /// buffer. If you'd rather use DMA with the blocking
392    /// [`embedded_io::Write`](crate::embedded_io::Write) trait, and avoid
393    /// having to use static buffers,
394    /// use[`Uart::with_tx_channel`](Self::with_tx_channel) instead.
395    #[inline]
396    #[hal_macro_helper]
397    pub fn send_with_dma<Ch, B>(
398        self,
399        buf: B,
400        mut channel: Ch,
401    ) -> Transfer<Channel<Ch::Id, Busy>, BufferPair<B, Self>>
402    where
403        Ch: AnyChannel<Status = Ready>,
404        B: Buffer<Beat = C::Word> + 'static,
405    {
406        channel
407            .as_mut()
408            .enable_interrupts(InterruptFlags::new().with_tcmpl(true));
409
410        #[hal_cfg("sercom0-d5x")]
411        let trigger_action = TriggerAction::Burst;
412
413        #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
414        let trigger_action = TriggerAction::Beat;
415
416        // SAFETY: This is safe because the of the `'static` bound check
417        // for `B`, and the fact that the buffer length of an `Uart` is always 1.
418        let xfer = unsafe { dmac::Transfer::new_unchecked(channel, buf, self, false) };
419        xfer.begin(C::Sercom::DMA_TX_TRIGGER, trigger_action)
420    }
421}
422
423//=============================================================================
424// SPI DMA transfers
425//=============================================================================
426
427unsafe impl<C, A> Buffer for Spi<C, A>
428where
429    C: spi::ValidConfig,
430    C::OpMode: spi::MasterMode,
431    C::Size: spi::AtomicSize<Word = C::Word>,
432    C::Word: Beat,
433    A: spi::Capability,
434{
435    type Beat = C::Word;
436
437    #[inline]
438    fn dma_ptr(&mut self) -> *mut Self::Beat {
439        self.data_ptr()
440    }
441
442    #[inline]
443    fn incrementing(&self) -> bool {
444        false
445    }
446
447    #[inline]
448    fn buffer_len(&self) -> usize {
449        1
450    }
451}
452
453impl<C, A> Spi<C, A>
454where
455    C: spi::ValidConfig,
456    A: spi::Transmit,
457    Self: Buffer<Beat = C::Word>,
458{
459    /// Transform an [`Spi`] into a DMA [`Transfer`]) and
460    /// start a send transaction.
461    #[inline]
462    #[hal_macro_helper]
463    #[deprecated(
464        since = "0.19.0",
465        note = "Use `Spi::with_dma_channels` instead. You will have access to DMA-enabled `embedded-hal` implementations."
466    )]
467    pub fn send_with_dma<Ch, B>(
468        self,
469        buf: B,
470        mut channel: Ch,
471    ) -> Transfer<Channel<Ch::Id, Busy>, BufferPair<B, Self>>
472    where
473        Ch: AnyChannel<Status = Ready>,
474        B: Buffer<Beat = C::Word> + 'static,
475    {
476        channel
477            .as_mut()
478            .enable_interrupts(InterruptFlags::new().with_tcmpl(true));
479
480        #[hal_cfg("sercom0-d5x")]
481        let trigger_action = TriggerAction::Burst;
482
483        #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
484        let trigger_action = TriggerAction::Beat;
485
486        // SAFETY: This is safe because the of the `'static` bound check
487        // for `B`, and the fact that the buffer length of an `Spi` is always 1.
488        let xfer = unsafe { Transfer::new_unchecked(channel, buf, self, false) };
489        xfer.begin(C::Sercom::DMA_TX_TRIGGER, trigger_action)
490    }
491}
492
493impl<C, A> Spi<C, A>
494where
495    C: spi::ValidConfig,
496    A: spi::Receive,
497    Self: Buffer<Beat = C::Word>,
498{
499    /// Transform an [`Spi`] into a DMA [`Transfer`]) and
500    /// start a receive transaction.
501    #[inline]
502    #[hal_macro_helper]
503    #[deprecated(
504        since = "0.19.0",
505        note = "Use `Spi::with_dma_channels` instead. You will have access to DMA-enabled `embedded-hal` implementations."
506    )]
507    pub fn receive_with_dma<Ch, B>(
508        self,
509        buf: B,
510        mut channel: Ch,
511    ) -> Transfer<Channel<Ch::Id, Busy>, BufferPair<Self, B>>
512    where
513        Ch: AnyChannel<Status = Ready>,
514        B: Buffer<Beat = C::Word> + 'static,
515    {
516        channel
517            .as_mut()
518            .enable_interrupts(InterruptFlags::new().with_tcmpl(true));
519
520        #[hal_cfg("sercom0-d5x")]
521        let trigger_action = TriggerAction::Burst;
522
523        #[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
524        let trigger_action = TriggerAction::Beat;
525
526        // SAFETY: This is safe because the of the `'static` bound check
527        // for `B`, and the fact that the buffer length of an `Spi` is always 1.
528        let xfer = unsafe { Transfer::new_unchecked(channel, self, buf, false) };
529        xfer.begin(C::Sercom::DMA_RX_TRIGGER, trigger_action)
530    }
531}
532
533/// Perform a SERCOM DMA read with a provided [`Buffer`]
534///
535/// # Safety
536///
537/// You **must** guarantee that the DMA transfer is either stopped or completed
538/// before giving back control of `channel` AND `buf`.
539#[hal_macro_helper]
540pub(super) unsafe fn read_dma<T, B, S>(
541    channel: &mut impl AnyChannel<Status = Ready>,
542    sercom_ptr: SercomPtr<T>,
543    buf: &mut B,
544) where
545    T: Beat,
546    B: Buffer<Beat = T>,
547    S: Sercom,
548{
549    read_dma_linked::<_, _, S>(channel, sercom_ptr, buf, None);
550}
551
552/// Perform a SERCOM DMA read with a provided [`Buffer`], and add an optional
553/// link to a next [`DmacDescriptor`] to support linked transfers.
554///
555/// # Safety
556///
557/// You **must** guarantee that the DMA transfer is either stopped or completed
558/// before giving back control of `channel` AND `buf`.
559#[hal_macro_helper]
560pub(super) unsafe fn read_dma_linked<T, B, S>(
561    channel: &mut impl AnyChannel<Status = Ready>,
562    mut sercom_ptr: SercomPtr<T>,
563    buf: &mut B,
564    next: Option<&mut DmacDescriptor>,
565) where
566    T: Beat,
567    B: Buffer<Beat = T>,
568    S: Sercom,
569{
570    #[hal_cfg("dmac-d5x")]
571    let trigger_action = TriggerAction::Burst;
572
573    #[hal_cfg(any("dmac-d11", "dmac-d21"))]
574    let trigger_action = TriggerAction::Beat;
575
576    // Safety: It is safe to bypass the buffer length check because `SercomPtr`
577    // always has a buffer length of 1.
578    channel.as_mut().transfer_unchecked(
579        &mut sercom_ptr,
580        buf,
581        S::DMA_RX_TRIGGER,
582        trigger_action,
583        next,
584    );
585}
586
587/// Perform a SERCOM DMA write with a provided [`Buffer`]
588///
589/// # Safety
590///
591/// You **must** guarantee that the DMA transfer is either stopped or completed
592/// before giving back control of `channel` AND `buf`.
593#[hal_macro_helper]
594pub(super) unsafe fn write_dma<T, B, S>(
595    channel: &mut impl AnyChannel<Status = Ready>,
596    sercom_ptr: SercomPtr<T>,
597    buf: &mut B,
598) where
599    T: Beat,
600    B: Buffer<Beat = T>,
601    S: Sercom,
602{
603    write_dma_linked::<_, _, S>(channel, sercom_ptr, buf, None);
604}
605
606/// Perform a SERCOM DMA write with a provided [`Buffer`], and add an optional
607/// link to a next [`DmacDescriptor`] to support linked transfers.
608///
609/// # Safety
610///
611/// You **must** guarantee that the DMA transfer is either stopped or completed
612/// before giving back control of `channel` AND `buf`.
613#[hal_macro_helper]
614pub(super) unsafe fn write_dma_linked<T, B, S>(
615    channel: &mut impl AnyChannel<Status = Ready>,
616    mut sercom_ptr: SercomPtr<T>,
617    buf: &mut B,
618    next: Option<&mut DmacDescriptor>,
619) where
620    T: Beat,
621    B: Buffer<Beat = T>,
622    S: Sercom,
623{
624    #[hal_cfg("dmac-d5x")]
625    let trigger_action = TriggerAction::Burst;
626
627    #[hal_cfg(any("dmac-d11", "dmac-d21"))]
628    let trigger_action = TriggerAction::Beat;
629
630    // Safety: It is safe to bypass the buffer length check because `SercomPtr`
631    // always has a buffer length of 1.
632    channel.as_mut().transfer_unchecked(
633        buf,
634        &mut sercom_ptr,
635        S::DMA_TX_TRIGGER,
636        trigger_action,
637        next,
638    );
639}
640
641/// Use the DMA Controller to perform async transfers using the SERCOM
642/// peripheral
643///
644/// See the [`mod@uart`], [`mod@i2c`] and [`mod@spi`] modules for the
645/// corresponding DMA transfer implementations.
646#[cfg(feature = "async")]
647pub(crate) mod async_dma {
648    use dmac::{Error, ReadyFuture};
649
650    use super::*;
651
652    /// Perform a SERCOM DMA read with a provided `&mut [T]`
653    #[inline]
654    pub(in super::super) async fn read_dma<T, B, S>(
655        channel: &mut impl AnyChannel<Status = ReadyFuture>,
656        sercom_ptr: SercomPtr<T>,
657        buf: &mut B,
658    ) -> Result<(), Error>
659    where
660        B: Buffer<Beat = T>,
661        T: Beat,
662        S: Sercom,
663    {
664        unsafe { read_dma_linked::<_, _, S>(channel, sercom_ptr, buf, None).await }
665    }
666
667    /// Perform a SERCOM DMA read with a provided [`Buffer`], and add an
668    /// optional link to a next [`DmacDescriptor`] to support linked
669    /// transfers.
670    ///
671    /// # Safety
672    ///
673    /// You **must** guarantee that the DMA transfer is either stopped or
674    /// completed before giving back control of `channel` AND `buf`.
675    #[inline]
676    #[hal_macro_helper]
677    pub(in super::super) async unsafe fn read_dma_linked<T, B, S>(
678        channel: &mut impl AnyChannel<Status = ReadyFuture>,
679        mut sercom_ptr: SercomPtr<T>,
680        buf: &mut B,
681        next: Option<&mut DmacDescriptor>,
682    ) -> Result<(), Error>
683    where
684        T: Beat,
685        B: Buffer<Beat = T>,
686        S: Sercom,
687    {
688        #[hal_cfg("dmac-d5x")]
689        let trigger_action = TriggerAction::Burst;
690
691        #[hal_cfg(any("dmac-d11", "dmac-d21"))]
692        let trigger_action = TriggerAction::Beat;
693
694        // Safety: It is safe to bypass the buffer length check because `SercomPtr`
695        // always has a buffer length of 1.
696        channel
697            .as_mut()
698            .transfer_future_linked(
699                &mut sercom_ptr,
700                buf,
701                S::DMA_RX_TRIGGER,
702                trigger_action,
703                next,
704            )
705            .await
706    }
707
708    /// Perform a SERCOM DMA write with a provided `&[T]`
709    #[inline]
710    pub(in super::super) async fn write_dma<T, B, S>(
711        channel: &mut impl AnyChannel<Status = ReadyFuture>,
712        sercom_ptr: SercomPtr<T>,
713        buf: &mut B,
714    ) -> Result<(), Error>
715    where
716        B: Buffer<Beat = T>,
717        T: Beat,
718        S: Sercom,
719    {
720        unsafe { write_dma_linked::<_, _, S>(channel, sercom_ptr, buf, None).await }
721    }
722
723    /// Perform a SERCOM DMA write with a provided [`Buffer`], and add an
724    /// optional link to a next [`DmacDescriptor`] to support linked
725    /// transfers.
726    ///
727    /// # Safety
728    ///
729    /// You **must** guarantee that the DMA transfer is either stopped or
730    /// completed before giving back control of `channel` AND `buf`.
731    #[inline]
732    #[hal_macro_helper]
733    pub(in super::super) async unsafe fn write_dma_linked<T, B, S>(
734        channel: &mut impl AnyChannel<Status = ReadyFuture>,
735        mut sercom_ptr: SercomPtr<T>,
736        buf: &mut B,
737        next: Option<&mut DmacDescriptor>,
738    ) -> Result<(), Error>
739    where
740        B: Buffer<Beat = T>,
741        T: Beat,
742        S: Sercom,
743    {
744        #[hal_cfg("dmac-d5x")]
745        let trigger_action = TriggerAction::Burst;
746
747        #[hal_cfg(any("dmac-d11", "dmac-d21"))]
748        let trigger_action = TriggerAction::Beat;
749
750        // Safety: It is safe to bypass the buffer length check because `SercomPtr`
751        // always has a buffer length of 1.
752        channel
753            .as_mut()
754            .transfer_future_linked(
755                buf,
756                &mut sercom_ptr,
757                S::DMA_TX_TRIGGER,
758                trigger_action,
759                next,
760            )
761            .await
762    }
763}