Skip to main content

esp_hal/dma/buffers/
mod.rs

1#[cfg(dma_can_access_psram)]
2use core::{mem::MaybeUninit, ops::Range};
3use core::{
4    ops::{Deref, DerefMut},
5    ptr::{NonNull, null_mut},
6};
7
8use super::*;
9#[cfg(dma_can_access_psram)]
10use crate::soc::{is_slice_in_psram, is_valid_psram_address, is_valid_ram_address};
11use crate::{
12    dma::aligned::{DmaAlignedMut, InternalMemory},
13    soc::is_slice_in_dram,
14};
15
16pub(crate) mod scoped;
17pub(crate) use scoped::*;
18
19/// Error returned from Dma[Rx|Tx|RxTx]Buf operations.
20#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub enum DmaBufError {
23    /// The buffer is smaller than the requested size.
24    BufferTooSmall,
25
26    /// More descriptors are needed for the buffer size.
27    InsufficientDescriptors,
28
29    /// Descriptors or buffers are not located in a supported memory region.
30    UnsupportedMemoryRegion,
31
32    /// Buffer address or size is not properly aligned.
33    InvalidAlignment(DmaAlignmentError),
34
35    /// Invalid chunk size: must be > 0 and <= 4095.
36    InvalidChunkSize,
37}
38
39impl core::fmt::Display for DmaBufError {
40    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
41        match self {
42            DmaBufError::BufferTooSmall => {
43                write!(f, "The buffer is smaller than the requested size")
44            }
45            DmaBufError::InsufficientDescriptors => {
46                write!(f, "More descriptors are needed for the buffer size")
47            }
48            DmaBufError::UnsupportedMemoryRegion => write!(
49                f,
50                "Descriptors or buffers are not located in a supported memory region"
51            ),
52            DmaBufError::InvalidAlignment(x) => write!(f, "{x}"),
53            DmaBufError::InvalidChunkSize => {
54                write!(f, "Invalid chunk size: must be > 0 and <= 4095")
55            }
56        }
57    }
58}
59
60impl core::error::Error for DmaBufError {}
61
62/// DMA buffer alignment errors.
63#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
64#[cfg_attr(feature = "defmt", derive(defmt::Format))]
65pub enum DmaAlignmentError {
66    /// Buffer address is not properly aligned.
67    Address,
68
69    /// Buffer size is not properly aligned.
70    Size,
71}
72
73impl From<DmaAlignmentError> for DmaBufError {
74    fn from(err: DmaAlignmentError) -> Self {
75        DmaBufError::InvalidAlignment(err)
76    }
77}
78
79impl core::fmt::Display for DmaAlignmentError {
80    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81        match self {
82            DmaAlignmentError::Address => write!(f, "Buffer address is not properly aligned"),
83            DmaAlignmentError::Size => write!(f, "Buffer size is not properly aligned"),
84        }
85    }
86}
87
88impl core::error::Error for DmaAlignmentError {}
89
90cfg_select! {
91    dma_can_access_psram => {
92        /// Burst size used when transferring to and from external memory.
93        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
94        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
95        pub enum ExternalBurstConfig {
96            /// 16 bytes
97            Size16 = 16,
98
99            /// 32 bytes
100            Size32 = 32,
101
102            /// 64 bytes
103            // TODO: investigate why ext_mem_bk_size = 2 causes corruption on S2
104            #[cfg(not(esp32s2))]
105            Size64 = 64,
106        }
107
108        impl ExternalBurstConfig {
109            /// The default external memory burst length.
110            pub const DEFAULT: Self = Self::Size16;
111        }
112
113        impl Default for ExternalBurstConfig {
114            fn default() -> Self {
115                Self::DEFAULT
116            }
117        }
118
119        /// Internal memory access burst mode.
120        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
121        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
122        pub enum InternalBurstConfig {
123            /// Burst mode is disabled.
124            Disabled,
125
126            /// Burst mode is enabled.
127            Enabled,
128        }
129
130        impl InternalBurstConfig {
131            /// The default internal burst mode configuration.
132            pub const DEFAULT: Self = Self::Disabled;
133        }
134
135        impl Default for InternalBurstConfig {
136            fn default() -> Self {
137                Self::DEFAULT
138            }
139        }
140
141        /// Burst transfer configuration.
142        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
143        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
144        pub struct BurstConfig {
145            /// Configures the burst size for PSRAM transfers.
146            ///
147            /// Burst mode is always enabled for PSRAM transfers.
148            pub external_memory: ExternalBurstConfig,
149
150            /// Enables or disables the burst mode for internal memory transfers.
151            ///
152            /// The burst size is not configurable.
153            pub internal_memory: InternalBurstConfig,
154        }
155
156        impl BurstConfig {
157            /// The default burst mode configuration.
158            pub const DEFAULT: Self = Self {
159                external_memory: ExternalBurstConfig::DEFAULT,
160                internal_memory: InternalBurstConfig::DEFAULT,
161            };
162        }
163
164        impl Default for BurstConfig {
165            fn default() -> Self {
166                Self::DEFAULT
167            }
168        }
169
170        impl From<InternalBurstConfig> for BurstConfig {
171            fn from(internal_memory: InternalBurstConfig) -> Self {
172                Self {
173                    external_memory: ExternalBurstConfig::DEFAULT,
174                    internal_memory,
175                }
176            }
177        }
178
179        impl From<ExternalBurstConfig> for BurstConfig {
180            fn from(external_memory: ExternalBurstConfig) -> Self {
181                Self {
182                    external_memory,
183                    internal_memory: InternalBurstConfig::DEFAULT,
184                }
185            }
186        }
187    }
188    _ => {
189        /// Burst transfer configuration.
190        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
191        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
192        pub enum BurstConfig {
193            /// Burst mode is disabled.
194            Disabled,
195
196            /// Burst mode is enabled.
197            Enabled,
198        }
199
200        impl BurstConfig {
201            /// The default burst mode configuration.
202            pub const DEFAULT: Self = Self::Disabled;
203        }
204
205        impl Default for BurstConfig {
206            fn default() -> Self {
207                Self::DEFAULT
208            }
209        }
210
211        type InternalBurstConfig = BurstConfig;
212    }
213}
214
215#[cfg(dma_can_access_psram)]
216impl ExternalBurstConfig {
217    const fn min_psram_alignment(self, direction: TransferDirection) -> usize {
218        // S2 TRM: Specifically, size and buffer address pointer in receive descriptors
219        // should be 16-byte, 32-byte or 64-byte aligned. For data frame whose
220        // length is not a multiple of 16 bytes, 32 bytes, or 64 bytes, EDMA adds
221        // padding bytes to the end.
222
223        // S3 TRM: Size and Address for IN transfers must be block aligned. For receive
224        // descriptors, if the data length received are not aligned with block size,
225        // GDMA will pad the data received with 0 until they are aligned to
226        // initiate burst transfer. You can read the length field in receive descriptors
227        // to obtain the length of valid data received
228        if matches!(direction, TransferDirection::In) {
229            self as usize
230        } else {
231            // S2 TRM: Size, length and buffer address pointer in transmit descriptors are
232            // not necessarily aligned with block size.
233
234            // S3 TRM: Size, length, and buffer address pointer in transmit descriptors do
235            // not need to be aligned.
236            1
237        }
238    }
239}
240
241impl InternalBurstConfig {
242    pub(super) const fn is_burst_enabled(self) -> bool {
243        !matches!(self, Self::Disabled)
244    }
245
246    // Size and address alignment as those come in pairs on current hardware.
247    const fn min_dram_alignment(self, direction: TransferDirection) -> usize {
248        if matches!(direction, TransferDirection::In) {
249            if cfg!(esp32) {
250                // NOTE: The size must be word-aligned.
251                // NOTE: The buffer address must be word-aligned
252                4
253            } else if self.is_burst_enabled() {
254                // As described in "Accessing Internal Memory" paragraphs in the various TRMs.
255                4
256            } else {
257                1
258            }
259        } else {
260            // OUT transfers have no alignment requirements, except for ESP32, which is
261            // described below.
262            if cfg!(esp32) {
263                // SPI DMA: Burst transmission is supported. The data size for
264                // a single transfer must be four bytes aligned.
265                // I2S DMA: Burst transfer is supported. However, unlike the
266                // SPI DMA channels, the data size for a single transfer is
267                // one word, or four bytes.
268                4
269            } else {
270                1
271            }
272        }
273    }
274}
275
276const fn max(a: usize, b: usize) -> usize {
277    if a > b { a } else { b }
278}
279
280impl BurstConfig {
281    delegate::delegate! {
282        to self.internal_memory {
283            #[cfg(dma_can_access_psram)]
284            pub(super) const fn min_dram_alignment(self, direction: TransferDirection) -> usize;
285
286            #[cfg(all(dma_can_access_psram, not(esp32s31)))] // Burst always enabled
287            pub(super) fn is_burst_enabled(self) -> bool;
288        }
289    }
290
291    /// Calculates an alignment that is compatible with the current burst
292    /// configuration.
293    ///
294    /// This is an over-estimation so that Descriptors can be safely used with
295    /// any DMA channel in any direction.
296    pub const fn min_compatible_alignment(self) -> usize {
297        let in_alignment = self.min_dram_alignment(TransferDirection::In);
298        let out_alignment = self.min_dram_alignment(TransferDirection::Out);
299        let alignment = max(in_alignment, out_alignment);
300
301        #[cfg(dma_can_access_psram)]
302        let alignment = max(alignment, self.external_memory as usize);
303
304        alignment
305    }
306
307    const fn chunk_size_for_alignment(alignment: usize) -> usize {
308        // DMA descriptors have a 12-bit field for the size/length of the buffer they
309        // point at. As there is no such thing as 0-byte alignment, this means the
310        // maximum size is 4095 bytes.
311        4096 - alignment
312    }
313
314    /// Calculates a chunk size that is compatible with the current burst
315    /// configuration's alignment requirements.
316    ///
317    /// This is an over-estimation so that Descriptors can be safely used with
318    /// any DMA channel in any direction.
319    pub const fn max_compatible_chunk_size(self) -> usize {
320        Self::chunk_size_for_alignment(self.min_compatible_alignment())
321    }
322
323    fn min_alignment(self, _buffer: &[u8], direction: TransferDirection) -> usize {
324        let alignment = self.min_dram_alignment(direction);
325
326        cfg_select! {
327            dma_can_access_psram => {
328                let mut alignment = alignment;
329                if is_valid_psram_address(_buffer.as_ptr() as usize) {
330                    alignment = max(
331                        alignment,
332                        self.external_memory.min_psram_alignment(direction),
333                    );
334                }
335            }
336            _ => {}
337        }
338
339        alignment
340    }
341
342    // Note: this function ignores address alignment as we assume the buffers are
343    // aligned.
344    fn max_chunk_size_for(self, buffer: &[u8], direction: TransferDirection) -> usize {
345        Self::chunk_size_for_alignment(self.min_alignment(buffer, direction))
346    }
347
348    fn ensure_buffer_aligned(
349        self,
350        buffer: &[u8],
351        direction: TransferDirection,
352    ) -> Result<(), DmaAlignmentError> {
353        let alignment = self.min_alignment(buffer, direction);
354        if !(buffer.as_ptr() as usize).is_multiple_of(alignment) {
355            return Err(DmaAlignmentError::Address);
356        }
357
358        // NB: the TRMs suggest that buffer length don't need to be aligned, but
359        // for IN transfers, we configure the DMA descriptors' size field, which needs
360        // to be aligned.
361        if direction == TransferDirection::In && !buffer.len().is_multiple_of(alignment) {
362            return Err(DmaAlignmentError::Size);
363        }
364
365        Ok(())
366    }
367
368    fn ensure_buffer_compatible(
369        self,
370        buffer: &[u8],
371        direction: TransferDirection,
372    ) -> Result<(), DmaBufError> {
373        if buffer.is_empty() {
374            return Ok(());
375        }
376        // buffer can be either DRAM or PSRAM (if supported)
377        let is_in_dram = is_slice_in_dram(buffer);
378        cfg_select! {
379            dma_can_access_psram => {
380                let is_in_psram = is_slice_in_psram(buffer);
381            }
382            _ => {
383                let is_in_psram = false;
384            }
385        }
386
387        if !(is_in_dram || is_in_psram) {
388            return Err(DmaBufError::UnsupportedMemoryRegion);
389        }
390
391        self.ensure_buffer_aligned(buffer, direction)?;
392
393        Ok(())
394    }
395}
396
397/// The direction of the DMA transfer.
398#[derive(Clone, Copy, PartialEq, Eq, Debug)]
399#[cfg_attr(feature = "defmt", derive(defmt::Format))]
400pub enum TransferDirection {
401    /// DMA transfer from peripheral or external memory to memory.
402    In,
403    /// DMA transfer from memory to peripheral or external memory.
404    Out,
405}
406
407/// Holds all the information needed to configure a DMA channel for a transfer.
408#[derive(PartialEq, Eq, Debug)]
409#[cfg_attr(feature = "defmt", derive(defmt::Format))]
410pub struct Preparation {
411    /// The descriptor the DMA will start from.
412    pub start: *mut DmaDescriptor,
413
414    /// Must be `true` if any of the DMA descriptors contain data in PSRAM.
415    #[cfg(dma_can_access_psram)]
416    pub accesses_psram: bool,
417
418    /// Configures the DMA to transfer data in bursts.
419    ///
420    /// The implementation of the buffer must ensure that buffer size
421    /// and alignment in each descriptor is compatible with the burst
422    /// transfer configuration.
423    ///
424    /// For details on alignment requirements, refer to the chip's
425    #[doc = crate::trm_markdown_link!()]
426    pub burst_transfer: BurstConfig,
427
428    /// Configures the "check owner" feature of the DMA channel.
429    ///
430    /// Most DMA channels allow software to configure whether the hardware
431    /// checks that [DmaDescriptor::owner] is set to [Owner::Dma] before
432    /// consuming the descriptor. If this check fails, the channel stops
433    /// operating and fires
434    /// [DmaRxInterrupt::DescriptorError]/[DmaTxInterrupt::DescriptorError].
435    ///
436    /// This field allows buffer implementation to configure this behavior.
437    /// - `Some(true)`: DMA channel must check the owner bit.
438    /// - `Some(false)`: DMA channel must NOT check the owner bit.
439    /// - `None`: DMA channel should check the owner bit if it is supported.
440    ///
441    /// Some buffer implementations may require that the DMA channel performs
442    /// this check before consuming the descriptor to ensure correct
443    /// behavior. e.g. To prevent wrap-around in a circular transfer.
444    ///
445    /// Some buffer implementations may require that the DMA channel does NOT
446    /// perform this check as the ownership bit will not be set before the
447    /// channel tries to consume the descriptor.
448    ///
449    /// Most implementations do not have any such requirements and work
450    /// correctly regardless of whether the DMA channel checks or not.
451    ///
452    /// If the DMA channel does not support the provided option, preparation
453    /// fails.
454    pub check_owner: Option<bool>,
455
456    /// Configures whether the DMA channel automatically clears the
457    /// [DmaDescriptor::owner] bit after it is done with the buffer pointed
458    /// to by a descriptor.
459    ///
460    /// For RX transfers, this is always true and the value specified here is
461    /// ignored.
462    ///
463    /// SPI_DMA on the ESP32 does not support this and panics if set
464    /// to true.
465    pub auto_write_back: bool,
466}
467
468/// [`DmaTxBuffer`] is a DMA descriptor + memory combo that can be used for
469/// transmitting data from a DMA channel to a peripheral's FIFO.
470///
471/// # Safety
472///
473/// The implementing type must keep all its descriptors and the buffers they
474/// point to valid while the buffer is being transferred.
475pub unsafe trait DmaTxBuffer {
476    /// A type providing operations that are safe to perform on the buffer
477    /// while the DMA is actively using it.
478    type View;
479
480    /// The type returned when a transfer finishes.
481    ///
482    /// Some buffers do not need to be reconstructed.
483    type Final;
484
485    /// Prepares the buffer for an imminent transfer and returns
486    /// information required to use this buffer.
487    ///
488    /// This operation is idempotent.
489    fn prepare(&mut self) -> Preparation;
490
491    /// This is called before the DMA starts using the buffer.
492    fn into_view(self) -> Self::View;
493
494    /// This is called after the DMA is done using the buffer.
495    fn from_view(view: Self::View) -> Self::Final;
496}
497
498/// [`DmaRxBuffer`] is a DMA descriptor + memory combo that can be used for
499/// receiving data from a peripheral's FIFO to a DMA channel.
500///
501/// Implementations of this trait may only support having a single EOF bit
502/// which resides in the last descriptor. There will be a separate trait in
503/// future to support multiple EOFs.
504///
505/// # Safety
506///
507/// The implementing type must keep all its descriptors and the buffers they
508/// point to valid while the buffer is being transferred.
509pub unsafe trait DmaRxBuffer {
510    /// A type providing operations that are safe to perform on the buffer
511    /// while the DMA is actively using it.
512    type View;
513
514    /// The type returned when a transfer finishes.
515    ///
516    /// Some buffers do not need to be reconstructed.
517    type Final;
518
519    /// Prepares the buffer for an imminent transfer and returns
520    /// information required to use this buffer.
521    ///
522    /// This operation is idempotent.
523    fn prepare(&mut self) -> Preparation;
524
525    /// This is called before the DMA starts using the buffer.
526    fn into_view(self) -> Self::View;
527
528    /// This is called after the DMA is done using the buffer.
529    fn from_view(view: Self::View) -> Self::Final;
530}
531
532/// An in-progress view into [`DmaRxBuf`]/[`DmaTxBuf`].
533///
534/// In the future, this could support peeking into state of the
535/// descriptors/buffers.
536pub struct BufView<T>(T);
537
538/// DMA transmit buffer
539///
540/// This is a contiguous buffer linked together by DMA descriptors of length
541/// 4095 at most. It can only be used for transmitting data to a peripheral's
542/// FIFO. See [`DmaRxBuf`] for receiving data.
543#[derive(Debug)]
544#[cfg_attr(feature = "defmt", derive(defmt::Format))]
545pub struct DmaTxBuf(ScopedDmaTxBuf<'static>);
546
547impl DmaTxBuf {
548    /// Creates a new [`DmaTxBuf`] from some descriptors and a buffer.
549    pub fn new(
550        descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
551        buffer: DmaAlignedMut<'static, [u8]>,
552    ) -> Result<Self, DmaBufError> {
553        ScopedDmaTxBuf::new(descriptors, buffer).map(Self)
554    }
555
556    /// Creates a new [`DmaTxBuf`] from some descriptors and a buffer.
557    ///
558    /// There must be enough descriptors for the provided buffer.
559    /// Depending on alignment requirements, each descriptor can handle at most
560    /// 4095 bytes worth of buffer.
561    ///
562    /// Both the descriptors and buffer must be in DMA-capable memory.
563    /// Only DRAM is supported for descriptors.
564    pub fn new_with_config(
565        descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
566        buffer: DmaAlignedMut<'static, [u8]>,
567        config: impl Into<BurstConfig>,
568    ) -> Result<Self, DmaBufError> {
569        ScopedDmaTxBuf::new_with_config(descriptors, buffer, config).map(Self)
570    }
571
572    /// Configures the DMA to use burst transfers to access this buffer.
573    pub fn set_burst_config(&mut self, burst: BurstConfig) -> Result<(), DmaBufError> {
574        self.0.set_burst_config(burst)
575    }
576
577    /// Consumes the buf, returning the descriptors and buffer.
578    pub fn split(
579        self,
580    ) -> (
581        DmaAlignedMut<'static, [DmaDescriptor]>,
582        DmaAlignedMut<'static, [u8]>,
583    ) {
584        self.0.split()
585    }
586
587    /// Returns the size of the underlying buffer.
588    pub fn capacity(&self) -> usize {
589        self.0.capacity()
590    }
591
592    /// Returns the number of bytes that would be transmitted by this buf.
593    #[allow(clippy::len_without_is_empty)]
594    pub fn len(&self) -> usize {
595        self.0.len()
596    }
597
598    /// Resets the descriptors to only transmit `len` amount of bytes from this
599    /// buf.
600    ///
601    /// The number of bytes in data must be less than or equal to the buffer
602    /// size.
603    pub fn set_length(&mut self, len: usize) {
604        self.0.set_length(len);
605    }
606
607    /// Fills the TX buffer with the bytes provided in `data` and reset the
608    /// descriptors to only cover the filled section.
609    ///
610    /// The number of bytes in data must be less than or equal to the buffer
611    /// size.
612    pub fn fill(&mut self, data: &[u8]) {
613        self.0.fill(data);
614    }
615
616    /// Returns the buf as a mutable slice that can be written.
617    pub fn as_mut_slice(&mut self) -> &mut [u8] {
618        self.0.as_mut_slice()
619    }
620
621    /// Returns the buf as a slice that can be read.
622    pub fn as_slice(&self) -> &[u8] {
623        self.0.as_slice()
624    }
625
626    /// Consumes the buffer and returns the scoped version.
627    pub(crate) fn into_scoped(self) -> ScopedDmaTxBuf<'static> {
628        self.0
629    }
630}
631
632unsafe impl DmaTxBuffer for DmaTxBuf {
633    type View = BufView<DmaTxBuf>;
634    type Final = DmaTxBuf;
635
636    fn prepare(&mut self) -> Preparation {
637        self.0.prepare()
638    }
639
640    fn into_view(self) -> BufView<DmaTxBuf> {
641        BufView(self)
642    }
643
644    fn from_view(view: Self::View) -> Self {
645        view.0
646    }
647}
648
649/// DMA receive buffer
650///
651/// This is a contiguous buffer linked together by DMA descriptors of length
652/// 4092. It can only be used for receiving data from a peripheral's FIFO.
653/// See [`DmaTxBuf`] for transmitting data.
654#[derive(Debug)]
655#[cfg_attr(feature = "defmt", derive(defmt::Format))]
656pub struct DmaRxBuf(ScopedDmaRxBuf<'static>);
657
658impl DmaRxBuf {
659    /// Creates a new [`DmaRxBuf`] from some descriptors and a buffer.
660    pub fn new(
661        descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
662        buffer: DmaAlignedMut<'static, [u8]>,
663    ) -> Result<Self, DmaBufError> {
664        ScopedDmaRxBuf::new(descriptors, buffer).map(Self)
665    }
666
667    /// Creates a new [`DmaRxBuf`] from some descriptors and a buffer.
668    ///
669    /// There must be enough descriptors for the provided buffer.
670    /// Depending on alignment requirements, each descriptor can handle at most
671    /// 4092 bytes worth of buffer.
672    ///
673    /// Both the descriptors and buffer must be in DMA-capable memory.
674    /// Only DRAM is supported for descriptors.
675    pub fn new_with_config(
676        descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
677        buffer: DmaAlignedMut<'static, [u8]>,
678        config: impl Into<BurstConfig>,
679    ) -> Result<Self, DmaBufError> {
680        ScopedDmaRxBuf::new_with_config(descriptors, buffer, config).map(Self)
681    }
682
683    /// Configures the DMA to use burst transfers to access this buffer.
684    pub fn set_burst_config(&mut self, burst: BurstConfig) -> Result<(), DmaBufError> {
685        self.0.set_burst_config(burst)
686    }
687
688    /// Consumes the buf, returning the descriptors and buffer.
689    pub fn split(
690        self,
691    ) -> (
692        DmaAlignedMut<'static, [DmaDescriptor]>,
693        DmaAlignedMut<'static, [u8]>,
694    ) {
695        self.0.split()
696    }
697
698    /// Returns the size of the underlying buffer.
699    pub fn capacity(&self) -> usize {
700        self.0.capacity()
701    }
702
703    /// Returns the maximum number of bytes that this buf has been configured to
704    /// receive.
705    #[allow(clippy::len_without_is_empty)]
706    pub fn len(&self) -> usize {
707        self.0.len()
708    }
709
710    /// Resets the descriptors to only receive `len` amount of bytes into this
711    /// buf.
712    ///
713    /// The number of bytes in data must be less than or equal to the buffer
714    /// size.
715    pub fn set_length(&mut self, len: usize) {
716        self.0.set_length(len)
717    }
718
719    /// Returns the entire underlying buffer as a slice that can be read.
720    pub fn as_slice(&self) -> &[u8] {
721        self.0.as_slice()
722    }
723
724    /// Returns the entire underlying buffer as a slice that can be written.
725    pub fn as_mut_slice(&mut self) -> &mut [u8] {
726        self.0.as_mut_slice()
727    }
728
729    /// Returns the number of bytes that was received by this buf.
730    pub fn number_of_received_bytes(&self) -> usize {
731        self.0.number_of_received_bytes()
732    }
733
734    /// Reads the received data into the provided `buf`.
735    ///
736    /// If `buf.len()` is less than the amount of received data then only the
737    /// first `buf.len()` bytes of received data is written into `buf`.
738    ///
739    /// Returns the number of bytes written to `buf`.
740    pub fn read_received_data(&self, buf: &mut [u8]) -> usize {
741        self.0.read_received_data(buf)
742    }
743
744    /// Returns the received data as an iterator of slices.
745    pub fn received_data(&self) -> impl Iterator<Item = &[u8]> {
746        self.0.received_data()
747    }
748
749    /// Consumes the buffer and returns the scoped version.
750    pub(crate) fn into_scoped(self) -> ScopedDmaRxBuf<'static> {
751        self.0
752    }
753}
754
755unsafe impl DmaRxBuffer for DmaRxBuf {
756    type View = BufView<DmaRxBuf>;
757    type Final = DmaRxBuf;
758
759    fn prepare(&mut self) -> Preparation {
760        self.0.prepare()
761    }
762
763    fn into_view(self) -> BufView<DmaRxBuf> {
764        BufView(self)
765    }
766
767    fn from_view(view: Self::View) -> Self {
768        view.0
769    }
770}
771
772/// DMA transmit and receive buffer.
773///
774/// This is a (single) contiguous buffer linked together by two sets of DMA
775/// descriptors of length 4092 each.
776/// It can be used for simultaneously transmitting to and receiving from a
777/// peripheral's FIFO. These are typically full-duplex transfers.
778#[derive(Debug)]
779#[cfg_attr(feature = "defmt", derive(defmt::Format))]
780pub struct DmaRxTxBuf {
781    rx_descriptors: DescriptorSet<'static>,
782    tx_descriptors: DescriptorSet<'static>,
783    buffer: DmaAlignedMut<'static, [u8]>,
784    burst: BurstConfig,
785}
786
787impl DmaRxTxBuf {
788    /// Creates a new [DmaRxTxBuf] from some descriptors and a buffer.
789    pub fn new(
790        rx_descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
791        tx_descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
792        buffer: DmaAlignedMut<'static, [u8]>,
793    ) -> Result<Self, DmaBufError> {
794        let mut buf = Self {
795            rx_descriptors: DescriptorSet::new(rx_descriptors)?,
796            tx_descriptors: DescriptorSet::new(tx_descriptors)?,
797            buffer,
798            burst: BurstConfig::default(),
799        };
800
801        let capacity = buf.capacity();
802        buf.configure(buf.burst, capacity)?;
803
804        Ok(buf)
805    }
806
807    fn configure(
808        &mut self,
809        burst: impl Into<BurstConfig>,
810        length: usize,
811    ) -> Result<(), DmaBufError> {
812        let burst = burst.into();
813        self.set_length_fallible(length, burst)?;
814
815        let max_chunk_size_in = burst.max_chunk_size_for(&self.buffer, TransferDirection::In);
816        let max_chunk_size_out = burst.max_chunk_size_for(&self.buffer, TransferDirection::Out);
817        self.rx_descriptors
818            .link_with_buffer(&mut self.buffer, max_chunk_size_in)?;
819        self.tx_descriptors
820            .link_with_buffer(&mut self.buffer, max_chunk_size_out)?;
821
822        self.burst = burst;
823
824        Ok(())
825    }
826
827    /// Configures the DMA to use burst transfers to access this buffer.
828    pub fn set_burst_config(&mut self, burst: BurstConfig) -> Result<(), DmaBufError> {
829        let len = self.len();
830        self.configure(burst, len)
831    }
832
833    /// Consumes the buf, returning the rx descriptors, tx descriptors and
834    /// buffer.
835    #[allow(clippy::type_complexity)]
836    pub fn split(
837        self,
838    ) -> (
839        DmaAlignedMut<'static, [DmaDescriptor]>,
840        DmaAlignedMut<'static, [DmaDescriptor]>,
841        DmaAlignedMut<'static, [u8]>,
842    ) {
843        (
844            self.rx_descriptors.into_inner(),
845            self.tx_descriptors.into_inner(),
846            self.buffer,
847        )
848    }
849
850    /// Returns the size of the underlying buffer.
851    pub fn capacity(&self) -> usize {
852        self.buffer.len()
853    }
854
855    /// Returns the number of bytes that would be transmitted by this buf.
856    #[allow(clippy::len_without_is_empty)]
857    pub fn len(&self) -> usize {
858        self.tx_descriptors
859            .linked_iter()
860            .map(|d| d.len())
861            .sum::<usize>()
862    }
863
864    /// Returns the entire buf as a slice that can be read.
865    pub fn as_slice(&self) -> &[u8] {
866        &self.buffer
867    }
868
869    /// Returns the entire buf as a slice that can be written.
870    pub fn as_mut_slice(&mut self) -> &mut [u8] {
871        &mut self.buffer
872    }
873
874    fn set_length_fallible(&mut self, len: usize, burst: BurstConfig) -> Result<(), DmaBufError> {
875        if len > self.capacity() {
876            return Err(DmaBufError::BufferTooSmall);
877        }
878        burst.ensure_buffer_compatible(&self.buffer[..len], TransferDirection::In)?;
879        burst.ensure_buffer_compatible(&self.buffer[..len], TransferDirection::Out)?;
880
881        let max_chunk_size_in = burst.max_chunk_size_for(&self.buffer, TransferDirection::In);
882        let max_chunk_size_out = burst.max_chunk_size_for(&self.buffer, TransferDirection::Out);
883        self.rx_descriptors.set_rx_length(len, max_chunk_size_in)?;
884        self.tx_descriptors.set_tx_length(len, max_chunk_size_out)?;
885
886        Ok(())
887    }
888
889    /// Resets the descriptors to only transmit/receive `len` amount of bytes
890    /// with this buf.
891    ///
892    /// `len` must be less than or equal to the buffer size.
893    pub fn set_length(&mut self, len: usize) {
894        unwrap!(self.set_length_fallible(len, self.burst));
895    }
896}
897
898unsafe impl DmaTxBuffer for DmaRxTxBuf {
899    type View = BufView<DmaRxTxBuf>;
900    type Final = DmaRxTxBuf;
901
902    fn prepare(&mut self) -> Preparation {
903        for desc in self.tx_descriptors.linked_iter_mut() {
904            // In non-circular mode, we only set `suc_eof` for the last descriptor to signal
905            // the end of the transfer.
906            desc.reset_for_tx(desc.next.is_null());
907        }
908
909        #[cfg(dma_can_access_psram)]
910        let is_data_in_psram = !is_valid_ram_address(self.buffer.as_ptr() as usize);
911
912        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
913        self.buffer.writeback();
914
915        Preparation {
916            start: self.tx_descriptors.head(),
917            #[cfg(dma_can_access_psram)]
918            accesses_psram: is_data_in_psram,
919            burst_transfer: self.burst,
920            check_owner: None,
921            auto_write_back: false,
922        }
923    }
924
925    fn into_view(self) -> BufView<DmaRxTxBuf> {
926        BufView(self)
927    }
928
929    fn from_view(view: Self::View) -> Self {
930        view.0
931    }
932}
933
934unsafe impl DmaRxBuffer for DmaRxTxBuf {
935    type View = BufView<DmaRxTxBuf>;
936    type Final = DmaRxTxBuf;
937
938    fn prepare(&mut self) -> Preparation {
939        for desc in self.rx_descriptors.linked_iter_mut() {
940            desc.reset_for_rx();
941        }
942
943        cfg_select! {
944            dma_can_access_psram => {
945                // Optimization: avoid locking for PSRAM range.
946                let is_data_in_psram = !is_valid_ram_address(self.buffer.as_ptr() as usize);
947                if is_data_in_psram || cfg!(soc_internal_memory_cached) {
948                    unsafe {
949                        crate::soc::cache_invalidate_addr(
950                            self.buffer.as_ptr() as u32,
951                            self.buffer.len() as u32,
952                        )
953                    };
954                }
955            }
956            _ => {}
957        }
958
959        Preparation {
960            start: self.rx_descriptors.head(),
961            #[cfg(dma_can_access_psram)]
962            accesses_psram: is_data_in_psram,
963            burst_transfer: self.burst,
964            check_owner: None,
965            auto_write_back: true,
966        }
967    }
968
969    fn into_view(self) -> BufView<DmaRxTxBuf> {
970        BufView(self)
971    }
972
973    fn from_view(view: Self::View) -> Self {
974        view.0
975    }
976}
977
978/// DMA Streaming Receive Buffer.
979///
980/// This is a contiguous buffer linked together by DMA descriptors, and the
981/// buffer is evenly distributed between each descriptor provided.
982///
983/// It is used for continuously streaming data from a peripheral's FIFO.
984///
985/// It maintains a sliding window of descriptors that progresses when
986/// [DmaRxStreamBufView::consume] is called.
987///
988/// The list starts out like so `A (empty) -> B (empty) -> C (empty) -> D
989/// (empty) -> NULL`.
990///
991/// As the DMA writes to the buffers the list progresses like so:
992/// - `A (empty) -> B (empty) -> C (empty) -> D (empty) -> NULL`
993/// - `A (full)  -> B (empty) -> C (empty) -> D (empty) -> NULL`
994/// - `A (full)  -> B (full)  -> C (empty) -> D (empty) -> NULL`
995/// - `A (full)  -> B (full)  -> C (full)  -> D (empty) -> NULL`
996///
997/// As [DmaRxStreamBufView::consume] is called, the list (approximately)
998/// progresses like so:
999/// - `A (full)  -> B (full)  -> C (full)  -> D (empty) -> NULL`
1000/// - `B (full)  -> C (full)  -> D (empty) -> A (empty) -> NULL`
1001/// - `C (full)  -> D (empty) -> A (empty) -> B (empty) -> NULL`
1002/// - `D (empty) -> A (empty) -> B (empty) -> C (empty) -> NULL`
1003///
1004/// If all the descriptors fill up, the [DmaRxInterrupt::DescriptorEmpty]
1005/// interrupt fires and the DMA stops writing. The transfer must then be resumed
1006/// or restarted.
1007///
1008/// This buffer does not indicate when this condition occurs. Check with the
1009/// driver to see if the DMA has stopped.
1010///
1011/// When constructing this buffer, tune the ratio between the chunk size and
1012/// buffer size appropriately. Smaller chunk sizes mean data is received more
1013/// frequently, but the DMA interrupts ([DmaRxInterrupt::Done]) also fire more
1014/// frequently when they are used.
1015///
1016/// See [DmaRxStreamBufView] for APIs available while a transfer is in
1017/// progress.
1018#[derive(Debug)]
1019#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1020pub struct DmaRxStreamBuf {
1021    descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
1022    buffer: DmaAlignedMut<'static, [u8]>,
1023    burst: BurstConfig,
1024}
1025
1026impl DmaRxStreamBuf {
1027    /// Creates a new [`DmaRxStreamBuf`] evenly distributing the buffer between
1028    /// the provided descriptors.
1029    pub fn new(
1030        mut descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
1031        mut buffer: DmaAlignedMut<'static, [u8]>,
1032    ) -> Result<Self, DmaBufError> {
1033        // see https://github.com/esp-rs/esp-hal/issues/2269#issuecomment-4397953660
1034        // we can lift that requirement once we sort out this issue
1035        if descriptors.len() < 4 {
1036            return Err(DmaBufError::InsufficientDescriptors);
1037        }
1038
1039        // Evenly distribute the buffer between the descriptors.
1040        let chunk_size = Some(buffer.len() / descriptors.len())
1041            .filter(|x| *x <= 4095)
1042            .ok_or(DmaBufError::InsufficientDescriptors)?;
1043
1044        let mut chunks = buffer.chunks_exact_mut(chunk_size);
1045        for (desc, chunk) in descriptors.iter_mut().zip(chunks.by_ref()) {
1046            desc.buffer = chunk.as_mut_ptr();
1047            desc.set_size(chunk.len());
1048        }
1049
1050        let remainder = chunks.into_remainder();
1051
1052        if !remainder.is_empty() {
1053            // Append any excess to the last descriptor.
1054            let last_descriptor = descriptors.last_mut().unwrap();
1055            let size = last_descriptor.size() + remainder.len();
1056            if size > 4095 {
1057                return Err(DmaBufError::InsufficientDescriptors);
1058            }
1059            last_descriptor.set_size(size);
1060        }
1061
1062        Ok(Self {
1063            descriptors,
1064            buffer,
1065            burst: BurstConfig::default(),
1066        })
1067    }
1068
1069    /// Consumes the buf, returning the descriptors and buffer.
1070    pub fn split(
1071        self,
1072    ) -> (
1073        DmaAlignedMut<'static, [DmaDescriptor]>,
1074        DmaAlignedMut<'static, [u8]>,
1075    ) {
1076        (self.descriptors, self.buffer)
1077    }
1078}
1079
1080unsafe impl DmaRxBuffer for DmaRxStreamBuf {
1081    type View = DmaRxStreamBufView;
1082    type Final = DmaRxStreamBuf;
1083
1084    fn prepare(&mut self) -> Preparation {
1085        // Link up all the descriptors (but not in a circle).
1086        let mut next = null_mut();
1087        for desc in self.descriptors.iter_mut().rev() {
1088            desc.next = next;
1089            next = desc;
1090
1091            desc.reset_for_rx();
1092        }
1093
1094        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1095        self.descriptors.writeback();
1096
1097        Preparation {
1098            start: self.descriptors.as_mut_ptr(),
1099            #[cfg(dma_can_access_psram)]
1100            accesses_psram: false,
1101            burst_transfer: self.burst,
1102
1103            // Whilst we give ownership of the descriptors the DMA, the correctness of this buffer
1104            // implementation doesn't rely on the DMA checking for descriptor ownership.
1105            // No descriptor is added back to the end of the stream before it's ready for the DMA
1106            // to consume it.
1107            check_owner: None,
1108            auto_write_back: true,
1109        }
1110    }
1111
1112    fn into_view(self) -> DmaRxStreamBufView {
1113        DmaRxStreamBufView {
1114            buf: self,
1115            descriptor_idx: 0,
1116            descriptor_offset: 0,
1117        }
1118    }
1119
1120    fn from_view(view: Self::View) -> Self {
1121        view.buf
1122    }
1123}
1124
1125/// A view into a [DmaRxStreamBuf].
1126pub struct DmaRxStreamBufView {
1127    buf: DmaRxStreamBuf,
1128    descriptor_idx: usize,
1129    descriptor_offset: usize,
1130}
1131
1132impl DmaRxStreamBufView {
1133    /// Returns the number of bytes that are available to read from the buf.
1134    pub fn available_bytes(&mut self) -> usize {
1135        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1136        self.buf.descriptors.invalidate();
1137
1138        let (tail, head) = self.buf.descriptors.split_at(self.descriptor_idx);
1139        let mut result = 0;
1140        for desc in head.iter().chain(tail) {
1141            if desc.owner() == Owner::Dma {
1142                break;
1143            }
1144            result += desc.len();
1145        }
1146        result - self.descriptor_offset
1147    }
1148
1149    /// Reads as much as possible into the buf from the available data.
1150    pub fn pop(&mut self, buf: &mut [u8]) -> usize {
1151        if buf.is_empty() {
1152            return 0;
1153        }
1154        let total_bytes = buf.len();
1155
1156        let mut remaining = buf;
1157        loop {
1158            let available = self.peek();
1159            if available.is_empty() {
1160                break;
1161            }
1162            if available.len() >= remaining.len() {
1163                remaining.copy_from_slice(&available[0..remaining.len()]);
1164                self.consume(remaining.len());
1165                let consumed = remaining.len();
1166                remaining = &mut remaining[consumed..];
1167                break;
1168            } else {
1169                let to_consume = available.len();
1170                remaining[0..to_consume].copy_from_slice(available);
1171                self.consume(to_consume);
1172                remaining = &mut remaining[to_consume..];
1173            }
1174        }
1175
1176        total_bytes - remaining.len()
1177    }
1178
1179    /// Returns a slice into the buffer containing available data.
1180    /// This will be the longest possible contiguous slice into the buffer that
1181    /// contains data that is available to read.
1182    ///
1183    /// Ignores EOFs. See [Self::peek_until_eof] for EOF support.
1184    pub fn peek(&mut self) -> &[u8] {
1185        let (slice, _) = self.peek_internal(false);
1186        slice
1187    }
1188
1189    /// Same as [Self::peek] but will not skip over any EOFs.
1190    ///
1191    /// It also returns a boolean indicating whether this slice ends with an EOF
1192    /// or not.
1193    pub fn peek_until_eof(&mut self) -> (&[u8], bool) {
1194        self.peek_internal(true)
1195    }
1196
1197    /// Consumes the first `n` bytes from the available data, returning any
1198    /// fully consumed descriptors back to the DMA.
1199    /// This is typically called after [Self::peek]/[Self::peek_until_eof].
1200    ///
1201    /// Returns the number of bytes that were actually consumed.
1202    pub fn consume(&mut self, n: usize) -> usize {
1203        let mut remaining_bytes_to_consume = n;
1204        let mut descriptors_modified = false;
1205
1206        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1207        self.buf.descriptors.invalidate();
1208
1209        loop {
1210            let desc = &mut self.buf.descriptors[self.descriptor_idx];
1211
1212            if desc.owner() == Owner::Dma {
1213                // Descriptor is still owned by DMA so it can't be read yet.
1214                // This should only happen when there is no more data available to read.
1215                break;
1216            }
1217
1218            let remaining_bytes_in_descriptor = desc.len() - self.descriptor_offset;
1219            if remaining_bytes_to_consume < remaining_bytes_in_descriptor {
1220                self.descriptor_offset += remaining_bytes_to_consume;
1221                remaining_bytes_to_consume = 0;
1222                break;
1223            }
1224
1225            // Reset the descriptor for reuse.
1226            desc.set_owner(Owner::Dma);
1227            desc.set_suc_eof(false);
1228            desc.set_length(0);
1229
1230            // Before connecting this descriptor to the end of the list, the next descriptor
1231            // must be disconnected from this one to prevent the DMA from
1232            // overtaking.
1233            desc.next = null_mut();
1234
1235            let desc_ptr: *mut _ = desc;
1236
1237            let prev_descriptor_index = self
1238                .descriptor_idx
1239                .checked_sub(1)
1240                .unwrap_or(self.buf.descriptors.len() - 1);
1241
1242            // Connect this consumed descriptor to the end of the chain.
1243            self.buf.descriptors[prev_descriptor_index].next = desc_ptr;
1244            descriptors_modified = true;
1245
1246            self.descriptor_idx += 1;
1247            if self.descriptor_idx >= self.buf.descriptors.len() {
1248                self.descriptor_idx = 0;
1249            }
1250            self.descriptor_offset = 0;
1251
1252            remaining_bytes_to_consume -= remaining_bytes_in_descriptor;
1253        }
1254
1255        if descriptors_modified {
1256            #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1257            self.buf.descriptors.writeback();
1258        }
1259
1260        n - remaining_bytes_to_consume
1261    }
1262
1263    fn peek_internal(&mut self, stop_at_eof: bool) -> (&[u8], bool) {
1264        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1265        self.buf.descriptors.invalidate();
1266
1267        let descriptors = &self.buf.descriptors[self.descriptor_idx..];
1268
1269        // There must be at least one descriptor.
1270        debug_assert!(!descriptors.is_empty());
1271
1272        if descriptors.len() == 1 {
1273            let last_descriptor = &descriptors[0];
1274            if last_descriptor.owner() == Owner::Dma {
1275                // No data available.
1276                (&[], false)
1277            } else {
1278                let length = last_descriptor.len() - self.descriptor_offset;
1279                let chunk_size = last_descriptor.size();
1280                let buffer_start = self.buf.buffer.len() - chunk_size;
1281                #[cfg(soc_internal_memory_cached)]
1282                if length != 0 {
1283                    unsafe {
1284                        crate::soc::cache_invalidate_addr(
1285                            self.buf.buffer.as_ptr().add(buffer_start) as u32,
1286                            length as u32,
1287                        );
1288                    }
1289                }
1290                (
1291                    &self.buf.buffer[buffer_start..][..length],
1292                    last_descriptor.flags.suc_eof(),
1293                )
1294            }
1295        } else {
1296            let chunk_size = descriptors[0].size();
1297            let mut found_eof = false;
1298
1299            let mut number_of_contiguous_bytes = 0;
1300            for desc in descriptors {
1301                if desc.owner() == Owner::Dma {
1302                    break;
1303                }
1304                number_of_contiguous_bytes += desc.len();
1305
1306                if stop_at_eof && desc.flags.suc_eof() {
1307                    found_eof = true;
1308                    break;
1309                }
1310                // If the length is smaller than the size, the contiguous-ness ends here.
1311                if desc.len() < desc.size() {
1312                    break;
1313                }
1314            }
1315
1316            #[cfg(soc_internal_memory_cached)]
1317            {
1318                let buffer_start = chunk_size * self.descriptor_idx + self.descriptor_offset;
1319                let buffer_len = number_of_contiguous_bytes - self.descriptor_offset;
1320                if buffer_len != 0 {
1321                    unsafe {
1322                        crate::soc::cache_invalidate_addr(
1323                            self.buf.buffer.as_ptr().add(buffer_start) as u32,
1324                            buffer_len as u32,
1325                        );
1326                    }
1327                }
1328            }
1329
1330            (
1331                &self.buf.buffer[chunk_size * self.descriptor_idx..][..number_of_contiguous_bytes]
1332                    [self.descriptor_offset..],
1333                found_eof,
1334            )
1335        }
1336    }
1337}
1338
1339/// DMA Streaming Transmit Buffer.
1340///
1341/// This is symmetric implementation to [DmaRxStreamBuf], used for continuously
1342/// streaming data to a peripheral's FIFO.
1343///
1344/// The list starts out like so `A(full) -> B(full) -> C(full) -> D(full) -> NULL`.
1345///
1346/// As the DMA writes to FIFO, the list progresses like so:
1347/// - `A(full)  -> B(full)  -> C(full)  -> D(full) -> NULL`
1348/// - `A(empty) -> B(full)  -> C(full)  -> D(full) -> NULL`
1349/// - `A(empty) -> B(empty) -> C(full)  -> D(full) -> NULL`
1350/// - `A(empty) -> B(empty) -> C(empty) -> D(full) -> NULL`
1351///
1352/// As [DmaTxStreamBufView::push] is called, the list (approximately) progresses like so:
1353/// - `A(empty) -> B(empty) -> C(empty) -> D(full) -> NULL`
1354/// - `B(empty) -> C(empty) -> D(full)  -> A(full) -> NULL`
1355/// - `C(empty) -> D(full)  -> A(full)  -> B(full) -> NULL`
1356/// - `D(full)  -> A(full)  -> B(full)  -> C(full) -> NULL`
1357///
1358/// If all the descriptors run out, the [DmaTxInterrupt::TotalEof] interrupt fires and the DMA
1359/// stops writing. The transfer must then be resumed or restarted.
1360#[derive(Debug)]
1361#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1362pub struct DmaTxStreamBuf {
1363    descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
1364    buffer: DmaAlignedMut<'static, [u8]>,
1365    burst: BurstConfig,
1366    pre_filled: Option<usize>,
1367    view_descriptor_idx: usize,
1368    view_descriptor_offset: usize,
1369}
1370
1371impl DmaTxStreamBuf {
1372    /// Creates a new [`DmaTxStreamBuf`] evenly distributing the buffer between
1373    /// the provided descriptors.
1374    pub fn new(
1375        mut descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
1376        mut buffer: DmaAlignedMut<'static, [u8]>,
1377    ) -> Result<Self, DmaBufError> {
1378        if descriptors.len() < 4 {
1379            // see https://github.com/esp-rs/esp-hal/issues/2269#issuecomment-4397953660
1380            // we can lift that requirement once we sort out this issue
1381            return Err(DmaBufError::InsufficientDescriptors);
1382        }
1383
1384        // Evenly distribute the buffer between the descriptors.
1385        let chunk_size = Some(buffer.len() / descriptors.len())
1386            .filter(|x| *x <= 4095)
1387            .ok_or(DmaBufError::InsufficientDescriptors)?;
1388
1389        let mut chunks = buffer.chunks_exact_mut(chunk_size);
1390        for (desc, chunk) in descriptors.iter_mut().zip(chunks.by_ref()) {
1391            desc.buffer = chunk.as_mut_ptr();
1392            desc.set_size(chunk.len());
1393            desc.set_length(chunk.len());
1394        }
1395        let remainder = chunks.into_remainder();
1396
1397        if !remainder.is_empty() {
1398            // Append any excess to the last descriptor.
1399            let last_descriptor = descriptors.last_mut().unwrap();
1400            let size = last_descriptor.size() + remainder.len();
1401            if size > 4095 {
1402                Err(DmaBufError::InsufficientDescriptors)?;
1403            }
1404            last_descriptor.set_size(size);
1405        }
1406
1407        Ok(Self {
1408            descriptors,
1409            buffer,
1410            burst: Default::default(),
1411            pre_filled: None,
1412            view_descriptor_idx: 0,
1413            view_descriptor_offset: 0,
1414        })
1415    }
1416
1417    /// Consumes the buf, returning the descriptors and buffer.
1418    pub fn split(
1419        self,
1420    ) -> (
1421        DmaAlignedMut<'static, [DmaDescriptor]>,
1422        DmaAlignedMut<'static, [u8]>,
1423    ) {
1424        (self.descriptors, self.buffer)
1425    }
1426
1427    /// Pushes the buffer with the given data before DMA transfer starts.
1428    ///
1429    /// It is expected to pre-fill at least enough data to fill the first two descriptors' buffers.
1430    /// The more data is pre-filled, the more head-room is left to push more data.
1431    pub fn push(&mut self, data: &[u8]) -> usize {
1432        self.push_with(|buf| {
1433            let len = buf.len().min(data.len());
1434            buf[..len].copy_from_slice(&data[..len]);
1435            len
1436        })
1437    }
1438
1439    /// Pushes the buffer with the given data before DMA transfer starts.
1440    ///
1441    /// It is expected to pre-fill at least enough data to fill the first two descriptors' buffers.
1442    /// The more data is pre-filled, the more head-room is left to push more data.
1443    ///
1444    /// Returns the number of bytes filled.
1445    pub fn push_with(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> usize {
1446        let start = self.pre_filled.unwrap_or(0);
1447        let bytes_pushed = f(&mut self.buffer[start..]);
1448        self.pre_filled = Some(start + bytes_pushed);
1449        bytes_pushed
1450    }
1451
1452    fn setup_view_state(&mut self) {
1453        let pre_filled = self.pre_filled.unwrap_or(self.buffer.len());
1454        let (idx, offset) = mark_tx_stream_descriptors_ready(&mut self.descriptors, pre_filled);
1455        self.view_descriptor_idx = idx;
1456        self.view_descriptor_offset = offset;
1457        #[cfg(soc_internal_memory_cached)]
1458        if pre_filled != 0 {
1459            unsafe {
1460                crate::soc::cache_writeback_addr(self.buffer.as_ptr() as u32, pre_filled as u32);
1461            }
1462        }
1463    }
1464}
1465
1466/// Marks descriptors containing data that should be transmitted when the DMA
1467/// channel starts.
1468fn mark_tx_stream_descriptors_ready(
1469    descriptors: &mut DmaAlignedMut<'_, [DmaDescriptor]>,
1470    bytes_pushed: usize,
1471) -> (usize, usize) {
1472    if bytes_pushed == 0 {
1473        return (0, 0);
1474    }
1475
1476    #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1477    descriptors.invalidate();
1478
1479    let num = descriptors.len();
1480    let mut bytes_filled = 0;
1481    let mut cursor = (0, 0);
1482
1483    for d in 0..num {
1484        let remaining = bytes_pushed - bytes_filled;
1485        let size = descriptors[d].size();
1486
1487        if remaining == 0 {
1488            terminate_tx_stream_at(descriptors, d);
1489            cursor = (d, 0);
1490            break;
1491        }
1492
1493        if remaining < size {
1494            if d == 0 {
1495                // The transfer needs at least one descriptor; send the partial chunk and
1496                // continue filling from the next one.
1497                descriptors[d].set_owner(Owner::Dma);
1498                descriptors[d].set_length(remaining);
1499                descriptors[d].set_suc_eof(true);
1500                if num > 1 {
1501                    terminate_tx_stream_at(descriptors, 1);
1502                    cursor = (1, 0);
1503                } else {
1504                    descriptors[d].next = null_mut();
1505                }
1506            } else {
1507                terminate_tx_stream_at(descriptors, d);
1508                cursor = (d, remaining);
1509            }
1510            break;
1511        }
1512
1513        bytes_filled += size;
1514        descriptors[d].set_owner(Owner::Dma);
1515        descriptors[d].set_length(size);
1516        descriptors[d].set_suc_eof(true);
1517    }
1518
1519    #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1520    descriptors.writeback();
1521
1522    cursor
1523}
1524
1525fn terminate_tx_stream_at(descriptors: &mut DmaAlignedMut<'_, [DmaDescriptor]>, start: usize) {
1526    if start > 0 {
1527        descriptors[start - 1].next = null_mut();
1528    }
1529    for desc in descriptors.iter_mut().skip(start) {
1530        desc.set_owner(Owner::Cpu);
1531    }
1532}
1533
1534fn advance_tx_stream_descriptors(
1535    descriptors: &mut DmaAlignedMut<'_, [DmaDescriptor]>,
1536    descriptor_idx: &mut usize,
1537    descriptor_offset: &mut usize,
1538    bytes_pushed: usize,
1539) {
1540    if bytes_pushed == 0 {
1541        return;
1542    }
1543
1544    let mut bytes_filled = 0;
1545    let num_descriptors = descriptors.len();
1546
1547    #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1548    descriptors.invalidate();
1549
1550    for i in 0..num_descriptors {
1551        let d = (*descriptor_idx + i) % num_descriptors;
1552        let desc = &mut descriptors[d];
1553        let bytes_in_d = desc.size() - *descriptor_offset;
1554        if bytes_in_d + bytes_filled > bytes_pushed {
1555            *descriptor_idx = d;
1556            *descriptor_offset = *descriptor_offset + bytes_pushed - bytes_filled;
1557            #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1558            descriptors.writeback();
1559            return;
1560        }
1561        bytes_filled += bytes_in_d;
1562        *descriptor_offset = 0;
1563
1564        // Put the current descriptor at the end of the list
1565        desc.set_owner(Owner::Dma);
1566        desc.set_length(desc.size());
1567        desc.set_suc_eof(true);
1568        let p = d.checked_sub(1).unwrap_or(num_descriptors - 1);
1569        if p != d {
1570            let [prev, desc] = descriptors.get_disjoint_mut([p, d]).unwrap();
1571            desc.next = null_mut();
1572            prev.next = desc;
1573        }
1574    }
1575
1576    #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1577    descriptors.writeback();
1578}
1579
1580unsafe impl DmaTxBuffer for DmaTxStreamBuf {
1581    type View = DmaTxStreamBufView;
1582    type Final = Self;
1583
1584    fn prepare(&mut self) -> Preparation {
1585        // Link up all the descriptors (but not in a circle).
1586        let mut next = null_mut();
1587        for desc in self.descriptors.iter_mut().rev() {
1588            desc.next = next;
1589            desc.set_owner(Owner::Dma);
1590            next = desc;
1591        }
1592        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1593        self.descriptors.writeback();
1594
1595        self.setup_view_state();
1596
1597        Preparation {
1598            start: self.descriptors.as_mut_ptr(),
1599            #[cfg(dma_can_access_psram)]
1600            accesses_psram: false,
1601            burst_transfer: self.burst,
1602
1603            // Whilst we give ownership of the descriptors the DMA, the correctness of this buffer
1604            // implementation doesn't rely on the DMA checking for descriptor ownership.
1605            // No descriptor is added back to the end of the stream before it's ready for the DMA
1606            // to consume it.
1607            check_owner: None,
1608            auto_write_back: true,
1609        }
1610    }
1611
1612    fn into_view(self) -> Self::View {
1613        DmaTxStreamBufView {
1614            descriptor_idx: self.view_descriptor_idx,
1615            descriptor_offset: self.view_descriptor_offset,
1616            buf: self,
1617        }
1618    }
1619
1620    fn from_view(view: Self::View) -> Self {
1621        let DmaTxStreamBufView {
1622            mut buf,
1623            descriptor_idx,
1624            descriptor_offset,
1625        } = view;
1626        buf.view_descriptor_idx = descriptor_idx;
1627        buf.view_descriptor_offset = descriptor_offset;
1628        buf
1629    }
1630}
1631
1632/// A view into a [DmaTxStreamBuf].
1633pub struct DmaTxStreamBufView {
1634    buf: DmaTxStreamBuf,
1635    descriptor_idx: usize,
1636    descriptor_offset: usize,
1637}
1638
1639impl DmaTxStreamBufView {
1640    /// Returns the number of bytes available for writing.
1641    pub fn available_bytes(&mut self) -> usize {
1642        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1643        self.buf.descriptors.invalidate();
1644
1645        let (tail, head) = self.buf.descriptors.split_at(self.descriptor_idx);
1646        head.iter()
1647            .chain(tail)
1648            .take_while(|d| d.owner() == Owner::Cpu)
1649            .map(|d| d.size())
1650            .sum::<usize>()
1651            .saturating_sub(self.descriptor_offset)
1652    }
1653
1654    fn write_position(&self) -> usize {
1655        let desc = &self.buf.descriptors[self.descriptor_idx];
1656        desc.buffer
1657            .addr()
1658            .wrapping_sub(self.buf.buffer.as_ptr().addr())
1659            + self.descriptor_offset
1660    }
1661
1662    /// Pushes a buffer into the stream buffer.
1663    /// Returns the number of bytes pushed.
1664    pub fn push_with(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> usize {
1665        let dma_start = self.write_position();
1666        let dma_end = dma_start
1667            .saturating_add(self.available_bytes())
1668            .min(self.buf.buffer.len())
1669            .max(dma_start);
1670        let bytes_pushed = f(&mut self.buf.buffer[dma_start..dma_end]).min(dma_end - dma_start);
1671        #[cfg(soc_internal_memory_cached)]
1672        if bytes_pushed != 0 {
1673            unsafe {
1674                crate::soc::cache_writeback_addr(
1675                    self.buf.buffer.as_ptr().add(dma_start) as u32,
1676                    bytes_pushed as u32,
1677                );
1678            }
1679        }
1680
1681        self.advance(bytes_pushed);
1682        bytes_pushed
1683    }
1684
1685    /// Advances the first `n` bytes from the available data.
1686    pub fn advance(&mut self, bytes_pushed: usize) {
1687        advance_tx_stream_descriptors(
1688            &mut self.buf.descriptors,
1689            &mut self.descriptor_idx,
1690            &mut self.descriptor_offset,
1691            bytes_pushed,
1692        );
1693    }
1694
1695    /// Pushes a buffer into the stream buffer.
1696    /// Returns the number of bytes pushed.
1697    pub fn push(&mut self, data: &[u8]) -> usize {
1698        let total_len = data.len();
1699        let mut remaining = data;
1700
1701        while !remaining.is_empty() && self.available_bytes() > 0 {
1702            let written = self.push_with(|buffer| {
1703                let len = usize::min(buffer.len(), remaining.len());
1704                buffer[..len].copy_from_slice(&remaining[..len]);
1705                len
1706            });
1707            if written == 0 {
1708                break;
1709            }
1710            remaining = &remaining[written..];
1711        }
1712
1713        total_len - remaining.len()
1714    }
1715}
1716
1717static mut EMPTY: InternalMemory<[DmaDescriptor; 1]> = InternalMemory::new([DmaDescriptor::EMPTY]);
1718
1719/// An empty buffer for transfers that carry no data.
1720pub struct EmptyBuf;
1721
1722unsafe impl DmaTxBuffer for EmptyBuf {
1723    type View = EmptyBuf;
1724    type Final = EmptyBuf;
1725
1726    fn prepare(&mut self) -> Preparation {
1727        #[cfg(soc_internal_memory_cached)]
1728        #[allow(static_mut_refs)]
1729        unsafe {
1730            EMPTY.get_mut().writeback();
1731        }
1732
1733        Preparation {
1734            start: (&raw mut EMPTY).cast(),
1735            #[cfg(dma_can_access_psram)]
1736            accesses_psram: false,
1737            burst_transfer: BurstConfig::default(),
1738
1739            // As we don't give ownership of the descriptor to the DMA, it's important that the DMA
1740            // channel does *NOT* check for ownership, otherwise the channel will return an error.
1741            check_owner: Some(false),
1742
1743            // The DMA should not write back to the descriptor as it is shared.
1744            auto_write_back: false,
1745        }
1746    }
1747
1748    fn into_view(self) -> EmptyBuf {
1749        self
1750    }
1751
1752    fn from_view(view: Self::View) -> Self {
1753        view
1754    }
1755}
1756
1757unsafe impl DmaRxBuffer for EmptyBuf {
1758    type View = EmptyBuf;
1759    type Final = EmptyBuf;
1760
1761    fn prepare(&mut self) -> Preparation {
1762        #[cfg(soc_internal_memory_cached)]
1763        #[allow(static_mut_refs)]
1764        unsafe {
1765            EMPTY.get_mut().writeback();
1766        }
1767
1768        Preparation {
1769            start: (&raw mut EMPTY).cast(),
1770            #[cfg(dma_can_access_psram)]
1771            accesses_psram: false,
1772            burst_transfer: BurstConfig::default(),
1773
1774            // As we don't give ownership of the descriptor to the DMA, it's important that the DMA
1775            // channel does *NOT* check for ownership, otherwise the channel will return an error.
1776            check_owner: Some(false),
1777            auto_write_back: true,
1778        }
1779    }
1780
1781    fn into_view(self) -> EmptyBuf {
1782        self
1783    }
1784
1785    fn from_view(view: Self::View) -> Self {
1786        view
1787    }
1788}
1789
1790/// DMA Loop Buffer
1791///
1792/// This consists of a single descriptor that points to itself and points to a
1793/// single buffer, resulting in the buffer being transmitted over and over
1794/// again, indefinitely.
1795///
1796/// A DMA descriptor is 12 bytes. If the buffer is significantly shorter
1797/// than this, the DMA channel spends more time reading the descriptor than
1798/// it does reading the buffer, which may leave it unable to keep up with the
1799/// bandwidth requirements of some peripherals at high frequencies.
1800pub struct DmaLoopBuf {
1801    descriptor: DmaAlignedMut<'static, [DmaDescriptor]>,
1802    buffer: DmaAlignedMut<'static, [u8]>,
1803}
1804
1805impl DmaLoopBuf {
1806    /// Creates a new [DmaLoopBuf].
1807    pub fn new(
1808        mut descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
1809        mut buffer: DmaAlignedMut<'static, [u8]>,
1810    ) -> Result<DmaLoopBuf, DmaBufError> {
1811        if buffer.len() > BurstConfig::default().max_chunk_size_for(&buffer, TransferDirection::Out)
1812        {
1813            return Err(DmaBufError::InsufficientDescriptors);
1814        }
1815
1816        descriptors[0].set_owner(Owner::Dma); // Doesn't matter
1817        descriptors[0].set_suc_eof(false);
1818        descriptors[0].set_length(buffer.len());
1819        descriptors[0].set_size(buffer.len());
1820        descriptors[0].buffer = buffer.as_mut_ptr();
1821        descriptors[0].next = descriptors.as_mut_ptr();
1822
1823        Ok(Self {
1824            descriptor: descriptors,
1825            buffer,
1826        })
1827    }
1828
1829    /// Consumes the buf, returning the descriptor and buffer.
1830    pub fn split(
1831        self,
1832    ) -> (
1833        DmaAlignedMut<'static, [DmaDescriptor]>,
1834        DmaAlignedMut<'static, [u8]>,
1835    ) {
1836        (self.descriptor, self.buffer)
1837    }
1838}
1839
1840unsafe impl DmaTxBuffer for DmaLoopBuf {
1841    type View = DmaLoopBuf;
1842    type Final = DmaLoopBuf;
1843
1844    fn prepare(&mut self) -> Preparation {
1845        Preparation {
1846            start: self.descriptor.as_mut_ptr(),
1847            #[cfg(dma_can_access_psram)]
1848            accesses_psram: false,
1849            burst_transfer: BurstConfig::default(),
1850            // The DMA must not check the owner bit, as it is never set.
1851            check_owner: Some(false),
1852
1853            // Doesn't matter either way but it is set to true for ESP32 SPI_DMA compatibility.
1854            auto_write_back: false,
1855        }
1856    }
1857
1858    fn into_view(self) -> Self::View {
1859        self
1860    }
1861
1862    fn from_view(view: Self::View) -> Self {
1863        view
1864    }
1865}
1866
1867impl Deref for DmaLoopBuf {
1868    type Target = [u8];
1869
1870    fn deref(&self) -> &Self::Target {
1871        &self.buffer
1872    }
1873}
1874
1875impl DerefMut for DmaLoopBuf {
1876    fn deref_mut(&mut self) -> &mut Self::Target {
1877        &mut self.buffer
1878    }
1879}
1880
1881/// A Preparation that masks itself as a DMA buffer.
1882///
1883/// For low-level use, where none of the pre-made buffers really fit.
1884///
1885/// Intended for low-level use inside esp-hal only.
1886pub(crate) struct NoBuffer(pub(crate) Preparation);
1887impl NoBuffer {
1888    fn prep(&self) -> Preparation {
1889        Preparation {
1890            start: self.0.start,
1891            #[cfg(dma_can_access_psram)]
1892            accesses_psram: self.0.accesses_psram,
1893            burst_transfer: self.0.burst_transfer,
1894            check_owner: self.0.check_owner,
1895            auto_write_back: self.0.auto_write_back,
1896        }
1897    }
1898}
1899unsafe impl DmaTxBuffer for NoBuffer {
1900    type View = ();
1901    type Final = ();
1902
1903    fn prepare(&mut self) -> Preparation {
1904        self.prep()
1905    }
1906
1907    fn into_view(self) -> Self::View {}
1908    fn from_view(_view: Self::View) {}
1909}
1910unsafe impl DmaRxBuffer for NoBuffer {
1911    type View = ();
1912    type Final = ();
1913
1914    fn prepare(&mut self) -> Preparation {
1915        self.prep()
1916    }
1917
1918    fn into_view(self) -> Self::View {}
1919    fn from_view(_view: Self::View) {}
1920}
1921
1922/// Prepares data unsafely to be transmitted via DMA.
1923///
1924/// `block_size` is the requirement imposed by the peripheral that receives the data. It
1925/// ensures that the DMA will not try to copy a partial block, which would cause the RX DMA (that
1926/// moves results back into RAM) to never complete.
1927///
1928/// The function returns the DMA buffer, and the number of bytes that will be transferred.
1929///
1930/// # Safety
1931///
1932/// The caller must keep all its descriptors and the buffers they
1933/// point to valid while the buffer is being transferred.
1934#[cfg_attr(not(any(aes_supports_dma, spi_master_supports_dma)), expect(unused))]
1935pub(crate) unsafe fn prepare_for_tx(
1936    descriptors: &mut [DmaDescriptor],
1937    mut data: NonNull<[u8]>,
1938    block_size: usize,
1939) -> Result<(NoBuffer, usize), DmaError> {
1940    let alignment =
1941        BurstConfig::DEFAULT.min_alignment(unsafe { data.as_ref() }, TransferDirection::Out);
1942
1943    if !data.addr().get().is_multiple_of(alignment) {
1944        // ESP32 has word alignment requirement on the TX descriptors, too.
1945        return Err(DmaError::InvalidAlignment(DmaAlignmentError::Address));
1946    }
1947
1948    // Whichever is stricter, data location or peripheral requirements.
1949    //
1950    // This ensures that the RX DMA, if used, can transfer the returned number of bytes using at
1951    // most N+2 descriptors. While the hardware doesn't require this on the TX DMA side, (the TX DMA
1952    // can, except on the ESP32, transfer any amount of data), it makes usage MUCH simpler.
1953    let alignment = alignment.max(block_size);
1954    let chunk_size = 4096 - alignment;
1955
1956    let data_len = data.len().min(chunk_size * descriptors.len());
1957
1958    cfg_select! {
1959        dma_can_access_psram => {
1960            let data_addr = data.addr().get();
1961            let data_in_psram = crate::psram::psram_range().contains(&data_addr);
1962
1963            // Make sure input data is in PSRAM instead of cache
1964            if data_in_psram || cfg!(soc_internal_memory_cached) {
1965                unsafe { crate::soc::cache_writeback_addr(data_addr as u32, data_len as u32) };
1966            }
1967        }
1968        soc_internal_memory_cached => {
1969            unsafe { crate::soc::cache_writeback_addr(data.addr().get() as u32, data_len as u32) };
1970        }
1971        _ => {}
1972    }
1973
1974    let descriptors = unsafe { DmaAlignedMut::new_unchecked(descriptors) };
1975    let mut descriptors = unwrap!(DescriptorSet::new(descriptors));
1976    // TODO: it would be best if this function returned the amount of data that could be linked
1977    // up.
1978    unwrap!(descriptors.link_with_buffer(unsafe { data.as_mut() }, chunk_size));
1979    unwrap!(descriptors.set_tx_length(data_len, chunk_size));
1980
1981    for desc in descriptors.linked_iter_mut() {
1982        desc.reset_for_tx(desc.next.is_null());
1983    }
1984
1985    #[cfg(soc_internal_memory_cached)]
1986    descriptors.descriptors.writeback();
1987
1988    Ok((
1989        NoBuffer(Preparation {
1990            start: descriptors.head(),
1991            burst_transfer: BurstConfig::DEFAULT,
1992            check_owner: None,
1993            auto_write_back: false,
1994            #[cfg(dma_can_access_psram)]
1995            accesses_psram: data_in_psram,
1996        }),
1997        data_len,
1998    ))
1999}
2000
2001/// Prepares buffers to receive data from DMA.
2002///
2003/// The function returns the DMA buffer, and the number of bytes that will be transferred.
2004///
2005/// # Safety
2006///
2007/// The caller must keep all its descriptors and the buffers they
2008/// point to valid while the buffer is being transferred.
2009#[cfg_attr(not(any(aes_supports_dma, spi_master_supports_dma)), expect(unused))]
2010pub(crate) unsafe fn prepare_for_rx(
2011    descriptors: &mut [DmaDescriptor],
2012    #[cfg(dma_can_access_psram)] align_buffers: &mut [Option<ManualWritebackBuffer>; 2],
2013    mut data: NonNull<[u8]>,
2014) -> (NoBuffer, usize) {
2015    let chunk_size =
2016        BurstConfig::DEFAULT.max_chunk_size_for(unsafe { data.as_ref() }, TransferDirection::In);
2017
2018    // The data we have to process may not be appropriate for the DMA:
2019    // - it may be improperly aligned for PSRAM
2020    // - it may not have a length that is a multiple of the external memory block size
2021
2022    cfg_select! {
2023        dma_can_access_psram => {
2024            let data_addr = data.addr().get();
2025            let data_in_psram = crate::psram::psram_range().contains(&data_addr);
2026        }
2027        _ => {
2028            let data_in_psram = false;
2029        }
2030    }
2031
2032    let descriptors = unsafe { DmaAlignedMut::new_unchecked(descriptors) };
2033    let mut descriptors = unwrap!(DescriptorSet::new(descriptors));
2034    let data_len = if data_in_psram {
2035        cfg_select! {
2036            dma_can_access_psram => {
2037                // This could use a better API, but right now we'll have to build the descriptor
2038                // list by hand.
2039                let consumed_bytes =
2040                    build_descriptor_list_for_psram(&mut descriptors, align_buffers, data);
2041
2042                // Invalidate data written by the DMA. As this likely affects more data than we
2043                // touched, write back first.
2044                unsafe {
2045                    crate::soc::cache_writeback_addr(data_addr as u32, consumed_bytes as u32);
2046                    crate::soc::cache_invalidate_addr(data_addr as u32, consumed_bytes as u32);
2047                }
2048
2049                consumed_bytes
2050            }
2051            _ => {
2052                unreachable!()
2053            }
2054        }
2055    } else {
2056        // Just set up descriptors as usual
2057        let data_len = data.len();
2058        unwrap!(descriptors.link_with_buffer(unsafe { data.as_mut() }, chunk_size));
2059        unwrap!(descriptors.set_tx_length(data_len, chunk_size));
2060
2061        #[cfg(soc_internal_memory_cached)]
2062        // Invalidate data written by the DMA. As this likely affects more data than we touched,
2063        // write back first.
2064        unsafe {
2065            crate::soc::cache_writeback_addr(data.addr().get() as u32, data_len as u32);
2066            crate::soc::cache_invalidate_addr(data.addr().get() as u32, data_len as u32);
2067        }
2068
2069        data_len
2070    };
2071
2072    for desc in descriptors.linked_iter_mut() {
2073        desc.reset_for_rx();
2074    }
2075
2076    #[cfg(soc_internal_memory_cached)]
2077    descriptors.descriptors.writeback();
2078
2079    (
2080        NoBuffer(Preparation {
2081            start: descriptors.head(),
2082            burst_transfer: BurstConfig::DEFAULT,
2083            check_owner: None,
2084            auto_write_back: true,
2085            #[cfg(dma_can_access_psram)]
2086            accesses_psram: data_in_psram,
2087        }),
2088        data_len,
2089    )
2090}
2091
2092#[cfg(dma_can_access_psram)]
2093fn build_descriptor_list_for_psram(
2094    descriptors: &mut DescriptorSet<'_>,
2095    copy_buffers: &mut [Option<ManualWritebackBuffer>; 2],
2096    data: NonNull<[u8]>,
2097) -> usize {
2098    let data_len = data.len();
2099    let data_addr = data.addr().get();
2100
2101    let min_alignment = ExternalBurstConfig::DEFAULT.min_psram_alignment(TransferDirection::In);
2102    let chunk_size = 4096 - min_alignment;
2103
2104    let mut desciptor_iter = DescriptorChainingIter::new(&mut descriptors.descriptors);
2105    let mut copy_buffer_iter = copy_buffers.iter_mut();
2106
2107    // MIN_LAST_DMA_LEN could make this really annoying, so we're just allocating a bit larger
2108    // buffer and shove edge cases into a single one. If we have >24 bytes on the S2, the 2-buffer
2109    // alignment algo works fine as one of them can steal 16 bytes, the other will have
2110    // MIN_LAST_DMA_LEN data to work with.
2111    let has_aligned_data = data_len > BUF_LEN;
2112
2113    // Calculate byte offset to the start of the buffer
2114    let offset = data_addr % min_alignment;
2115    let head_to_copy = min_alignment - offset;
2116    let head_to_copy = if !has_aligned_data {
2117        BUF_LEN
2118    } else if head_to_copy > 0 && head_to_copy < MIN_LAST_DMA_LEN {
2119        head_to_copy + min_alignment
2120    } else {
2121        head_to_copy
2122    };
2123    let head_to_copy = head_to_copy.min(data_len);
2124
2125    // Calculate last unaligned part
2126    let tail_to_copy = (data_len - head_to_copy) % min_alignment;
2127    let tail_to_copy = if tail_to_copy > 0 && tail_to_copy < MIN_LAST_DMA_LEN {
2128        tail_to_copy + min_alignment
2129    } else {
2130        tail_to_copy
2131    };
2132
2133    let mut consumed = 0;
2134
2135    // Align beginning
2136    if head_to_copy > 0 {
2137        let copy_buffer = unwrap!(copy_buffer_iter.next());
2138        let buffer =
2139            copy_buffer.insert(ManualWritebackBuffer::new(get_range(data, 0..head_to_copy)));
2140        buffer.prepare_for_dma();
2141
2142        let Some(descriptor) = desciptor_iter.next() else {
2143            return consumed;
2144        };
2145        descriptor.set_size(head_to_copy);
2146        descriptor.buffer = buffer.mut_buffer_ptr();
2147        consumed += head_to_copy;
2148    };
2149
2150    // Chain up descriptors for the main aligned data part.
2151    let mut aligned_data = get_range(data, head_to_copy..data.len() - tail_to_copy);
2152    while !aligned_data.is_empty() {
2153        let Some(descriptor) = desciptor_iter.next() else {
2154            return consumed;
2155        };
2156        let chunk = aligned_data.len().min(chunk_size);
2157
2158        descriptor.set_size(chunk);
2159        descriptor.buffer = aligned_data.cast::<u8>().as_ptr();
2160        consumed += chunk;
2161        aligned_data = get_range(aligned_data, chunk..aligned_data.len());
2162    }
2163
2164    // Align end
2165    if tail_to_copy > 0 {
2166        let copy_buffer = unwrap!(copy_buffer_iter.next());
2167        let buffer = copy_buffer.insert(ManualWritebackBuffer::new(get_range(
2168            data,
2169            data.len() - tail_to_copy..data.len(),
2170        )));
2171        buffer.prepare_for_dma();
2172
2173        let Some(descriptor) = desciptor_iter.next() else {
2174            return consumed;
2175        };
2176        descriptor.set_size(tail_to_copy);
2177        descriptor.buffer = buffer.mut_buffer_ptr();
2178        consumed += tail_to_copy;
2179    }
2180
2181    consumed
2182}
2183
2184#[cfg(dma_can_access_psram)]
2185fn get_range(ptr: NonNull<[u8]>, range: Range<usize>) -> NonNull<[u8]> {
2186    let len = range.end - range.start;
2187    NonNull::slice_from_raw_parts(unsafe { ptr.cast().byte_add(range.start) }, len)
2188}
2189
2190#[cfg(dma_can_access_psram)]
2191struct DescriptorChainingIter<'a> {
2192    /// index of the next element to emit
2193    index: usize,
2194    descriptors: &'a mut [DmaDescriptor],
2195}
2196#[cfg(dma_can_access_psram)]
2197impl<'a> DescriptorChainingIter<'a> {
2198    fn new(descriptors: &'a mut [DmaDescriptor]) -> Self {
2199        Self {
2200            descriptors,
2201            index: 0,
2202        }
2203    }
2204
2205    fn next(&mut self) -> Option<&'_ mut DmaDescriptor> {
2206        if self.index == 0 {
2207            self.index += 1;
2208            self.descriptors.get_mut(0)
2209        } else if self.index < self.descriptors.len() {
2210            let index = self.index;
2211            self.index += 1;
2212
2213            // Grab a pointer to the current descriptor.
2214            let ptr = &raw mut self.descriptors[index];
2215
2216            // Link the descriptor to the previous one.
2217            self.descriptors[index - 1].next = ptr;
2218
2219            // Reborrow the pointer so that it doesn't get invalidated by our continued use of the
2220            // descriptor reference.
2221            Some(unsafe { &mut *ptr })
2222        } else {
2223            None
2224        }
2225    }
2226}
2227
2228#[cfg(dma_can_access_psram)]
2229const MIN_LAST_DMA_LEN: usize = if cfg!(esp32s2) { 5 } else { 1 };
2230#[cfg(dma_can_access_psram)]
2231const BUF_LEN: usize = 16 + 2 * (MIN_LAST_DMA_LEN - 1); // 2x makes aligning short buffers simpler
2232
2233/// PSRAM helper. DMA can write data of any alignment into this buffer, and it can be written by
2234/// the CPU back to PSRAM.
2235#[cfg(dma_can_access_psram)]
2236pub(crate) struct ManualWritebackBuffer {
2237    buffer: InternalMemory<MaybeUninit<[u8; BUF_LEN]>>,
2238    dst_address: NonNull<u8>,
2239    n_bytes: u8,
2240}
2241
2242#[cfg(dma_can_access_psram)]
2243impl ManualWritebackBuffer {
2244    pub fn new(ptr: NonNull<[u8]>) -> Self {
2245        assert!(ptr.len() <= BUF_LEN);
2246        Self {
2247            buffer: InternalMemory::new(MaybeUninit::uninit()),
2248            dst_address: ptr.cast(),
2249            n_bytes: ptr.len() as u8,
2250        }
2251    }
2252
2253    pub fn prepare_for_dma(&mut self) {
2254        // Ensure our cache line is not dirty. A dirty cacheline
2255        // evicted during DMA operation can clobber received data.
2256        #[cfg(soc_internal_memory_cached)]
2257        self.buffer.get_mut().invalidate();
2258    }
2259
2260    pub fn write_back(&mut self) {
2261        // The DMA wrote its data directly to memory, bypassing the CPU cache.
2262        // Invalidate the cache lines covering the alignment buffer so the CPU
2263        // reads the fresh DMA data rather than the stale zeros from new().
2264        #[cfg(soc_internal_memory_cached)]
2265        self.buffer.get_mut().invalidate();
2266
2267        let src = self.mut_buffer_ptr().cast_const();
2268        unsafe {
2269            self.dst_address
2270                .as_ptr()
2271                .copy_from(src, self.n_bytes as usize);
2272        }
2273    }
2274
2275    pub fn mut_buffer_ptr(&mut self) -> *mut u8 {
2276        self.buffer.get_mut().as_mut_ptr().cast::<u8>()
2277    }
2278}