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 your 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 behaviour.
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    /// behaviour. 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 won't have any such requirements and will work
450    /// correctly regardless of whether the DMA channel checks or not.
451    ///
452    /// Note: If the DMA channel doesn't support the provided option,
453    /// preparation will fail.
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    /// Note: SPI_DMA on the ESP32 does not support this and will panic 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    /// whilst the DMA is actively using it.
478    type View;
479
480    /// The type returned to the user when a transfer finishes.
481    ///
482    /// Some buffers don't 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    /// Note: 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/// Note: 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    /// whilst the DMA is actively using it.
512    type View;
513
514    /// The type returned to the user when a transfer finishes.
515    ///
516    /// Some buffers don't 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    /// Note: 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    /// Consume 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    /// Return 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    /// Reset 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 than 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 than 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    /// Consume 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    /// Reset 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 than 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 than can be written.
725    pub fn as_mut_slice(&mut self) -> &mut [u8] {
726        self.0.as_mut_slice()
727    }
728
729    /// Return 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 in 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    /// Consume 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    /// Return the size of the underlying buffer.
851    pub fn capacity(&self) -> usize {
852        self.buffer.len()
853    }
854
855    /// Return 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 than can be read.
865    pub fn as_slice(&self) -> &[u8] {
866        &self.buffer
867    }
868
869    /// Returns the entire buf as a slice than 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    /// Reset 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 does so by maintaining sliding window of descriptors that progresses when
986/// you call [DmaRxStreamBufView::consume].
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 you call [DmaRxStreamBufView::consume] 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 will fire and the DMA will stop writing, at which point it is up
1006/// to you to resume/restart the transfer.
1007///
1008/// Note: This buffer will not tell you when this condition occurs, you should
1009/// check with the driver to see if the DMA has stopped.
1010///
1011/// When constructing this buffer, it is important to tune the ratio between the
1012/// chunk size and buffer size appropriately. Smaller chunk sizes means you
1013/// receive data more frequently but this means the DMA interrupts
1014/// ([DmaRxInterrupt::Done]) also fire more frequently (if you use them).
1015///
1016/// See [DmaRxStreamBufView] for APIs available whilst 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    /// Consume 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    /// Note: This function ignores EOFs, see [Self::peek_until_eof] if you need
1184    /// EOF support.
1185    pub fn peek(&mut self) -> &[u8] {
1186        let (slice, _) = self.peek_internal(false);
1187        slice
1188    }
1189
1190    /// Same as [Self::peek] but will not skip over any EOFs.
1191    ///
1192    /// It also returns a boolean indicating whether this slice ends with an EOF
1193    /// or not.
1194    pub fn peek_until_eof(&mut self) -> (&[u8], bool) {
1195        self.peek_internal(true)
1196    }
1197
1198    /// Consumes the first `n` bytes from the available data, returning any
1199    /// fully consumed descriptors back to the DMA.
1200    /// This is typically called after [Self::peek]/[Self::peek_until_eof].
1201    ///
1202    /// Returns the number of bytes that were actually consumed.
1203    pub fn consume(&mut self, n: usize) -> usize {
1204        let mut remaining_bytes_to_consume = n;
1205        let mut descriptors_modified = false;
1206
1207        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1208        self.buf.descriptors.invalidate();
1209
1210        loop {
1211            let desc = &mut self.buf.descriptors[self.descriptor_idx];
1212
1213            if desc.owner() == Owner::Dma {
1214                // Descriptor is still owned by DMA so it can't be read yet.
1215                // This should only happen when there is no more data available to read.
1216                break;
1217            }
1218
1219            let remaining_bytes_in_descriptor = desc.len() - self.descriptor_offset;
1220            if remaining_bytes_to_consume < remaining_bytes_in_descriptor {
1221                self.descriptor_offset += remaining_bytes_to_consume;
1222                remaining_bytes_to_consume = 0;
1223                break;
1224            }
1225
1226            // Reset the descriptor for reuse.
1227            desc.set_owner(Owner::Dma);
1228            desc.set_suc_eof(false);
1229            desc.set_length(0);
1230
1231            // Before connecting this descriptor to the end of the list, the next descriptor
1232            // must be disconnected from this one to prevent the DMA from
1233            // overtaking.
1234            desc.next = null_mut();
1235
1236            let desc_ptr: *mut _ = desc;
1237
1238            let prev_descriptor_index = self
1239                .descriptor_idx
1240                .checked_sub(1)
1241                .unwrap_or(self.buf.descriptors.len() - 1);
1242
1243            // Connect this consumed descriptor to the end of the chain.
1244            self.buf.descriptors[prev_descriptor_index].next = desc_ptr;
1245            descriptors_modified = true;
1246
1247            self.descriptor_idx += 1;
1248            if self.descriptor_idx >= self.buf.descriptors.len() {
1249                self.descriptor_idx = 0;
1250            }
1251            self.descriptor_offset = 0;
1252
1253            remaining_bytes_to_consume -= remaining_bytes_in_descriptor;
1254        }
1255
1256        if descriptors_modified {
1257            #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1258            self.buf.descriptors.writeback();
1259        }
1260
1261        n - remaining_bytes_to_consume
1262    }
1263
1264    fn peek_internal(&mut self, stop_at_eof: bool) -> (&[u8], bool) {
1265        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1266        self.buf.descriptors.invalidate();
1267
1268        let descriptors = &self.buf.descriptors[self.descriptor_idx..];
1269
1270        // There must be at least one descriptor.
1271        debug_assert!(!descriptors.is_empty());
1272
1273        if descriptors.len() == 1 {
1274            let last_descriptor = &descriptors[0];
1275            if last_descriptor.owner() == Owner::Dma {
1276                // No data available.
1277                (&[], false)
1278            } else {
1279                let length = last_descriptor.len() - self.descriptor_offset;
1280                let chunk_size = last_descriptor.size();
1281                let buffer_start = self.buf.buffer.len() - chunk_size;
1282                #[cfg(soc_internal_memory_cached)]
1283                if length != 0 {
1284                    unsafe {
1285                        crate::soc::cache_invalidate_addr(
1286                            self.buf.buffer.as_ptr().add(buffer_start) as u32,
1287                            length as u32,
1288                        );
1289                    }
1290                }
1291                (
1292                    &self.buf.buffer[buffer_start..][..length],
1293                    last_descriptor.flags.suc_eof(),
1294                )
1295            }
1296        } else {
1297            let chunk_size = descriptors[0].size();
1298            let mut found_eof = false;
1299
1300            let mut number_of_contiguous_bytes = 0;
1301            for desc in descriptors {
1302                if desc.owner() == Owner::Dma {
1303                    break;
1304                }
1305                number_of_contiguous_bytes += desc.len();
1306
1307                if stop_at_eof && desc.flags.suc_eof() {
1308                    found_eof = true;
1309                    break;
1310                }
1311                // If the length is smaller than the size, the contiguous-ness ends here.
1312                if desc.len() < desc.size() {
1313                    break;
1314                }
1315            }
1316
1317            #[cfg(soc_internal_memory_cached)]
1318            {
1319                let buffer_start = chunk_size * self.descriptor_idx + self.descriptor_offset;
1320                let buffer_len = number_of_contiguous_bytes - self.descriptor_offset;
1321                if buffer_len != 0 {
1322                    unsafe {
1323                        crate::soc::cache_invalidate_addr(
1324                            self.buf.buffer.as_ptr().add(buffer_start) as u32,
1325                            buffer_len as u32,
1326                        );
1327                    }
1328                }
1329            }
1330
1331            (
1332                &self.buf.buffer[chunk_size * self.descriptor_idx..][..number_of_contiguous_bytes]
1333                    [self.descriptor_offset..],
1334                found_eof,
1335            )
1336        }
1337    }
1338}
1339
1340/// DMA Streaming Transmit Buffer.
1341///
1342/// This is symmetric implementation to [DmaRxStreamBuf], used for continuously
1343/// streaming data to a peripheral's FIFO.
1344///
1345/// The list starts out like so `A(full) -> B(full) -> C(full) -> D(full) -> NULL`.
1346///
1347/// As the DMA writes to FIFO, the list progresses like so:
1348/// - `A(full)  -> B(full)  -> C(full)  -> D(full) -> NULL`
1349/// - `A(empty) -> B(full)  -> C(full)  -> D(full) -> NULL`
1350/// - `A(empty) -> B(empty) -> C(full)  -> D(full) -> NULL`
1351/// - `A(empty) -> B(empty) -> C(empty) -> D(full) -> NULL`
1352///
1353/// As you call [DmaTxStreamBufView::push] the list (approximately) progresses like so:
1354/// - `A(empty) -> B(empty) -> C(empty) -> D(full) -> NULL`
1355/// - `B(empty) -> C(empty) -> D(full)  -> A(full) -> NULL`
1356/// - `C(empty) -> D(full)  -> A(full)  -> B(full) -> NULL`
1357/// - `D(full)  -> A(full)  -> B(full)  -> C(full) -> NULL`
1358///
1359/// If all the descriptors run out, the [DmaTxInterrupt::TotalEof] interrupt will fire and DMA
1360/// will stop writing, at which point it is up to you to resume/restart the transfer.
1361#[derive(Debug)]
1362#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1363pub struct DmaTxStreamBuf {
1364    descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
1365    buffer: DmaAlignedMut<'static, [u8]>,
1366    burst: BurstConfig,
1367    pre_filled: Option<usize>,
1368    view_descriptor_idx: usize,
1369    view_descriptor_offset: usize,
1370}
1371
1372impl DmaTxStreamBuf {
1373    /// Creates a new [DmaTxStreamBuf] evenly distributing the buffer between
1374    /// the provided descriptors.
1375    pub fn new(
1376        mut descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
1377        mut buffer: DmaAlignedMut<'static, [u8]>,
1378    ) -> Result<Self, DmaBufError> {
1379        if descriptors.len() < 4 {
1380            // see https://github.com/esp-rs/esp-hal/issues/2269#issuecomment-4397953660
1381            // we can lift that requirement once we sort out this issue
1382            return Err(DmaBufError::InsufficientDescriptors);
1383        }
1384
1385        // Evenly distribute the buffer between the descriptors.
1386        let chunk_size = Some(buffer.len() / descriptors.len())
1387            .filter(|x| *x <= 4095)
1388            .ok_or(DmaBufError::InsufficientDescriptors)?;
1389
1390        let mut chunks = buffer.chunks_exact_mut(chunk_size);
1391        for (desc, chunk) in descriptors.iter_mut().zip(chunks.by_ref()) {
1392            desc.buffer = chunk.as_mut_ptr();
1393            desc.set_size(chunk.len());
1394            desc.set_length(chunk.len());
1395        }
1396        let remainder = chunks.into_remainder();
1397
1398        if !remainder.is_empty() {
1399            // Append any excess to the last descriptor.
1400            let last_descriptor = descriptors.last_mut().unwrap();
1401            let size = last_descriptor.size() + remainder.len();
1402            if size > 4095 {
1403                Err(DmaBufError::InsufficientDescriptors)?;
1404            }
1405            last_descriptor.set_size(size);
1406        }
1407
1408        Ok(Self {
1409            descriptors,
1410            buffer,
1411            burst: Default::default(),
1412            pre_filled: None,
1413            view_descriptor_idx: 0,
1414            view_descriptor_offset: 0,
1415        })
1416    }
1417
1418    /// Consume the buf, returning the descriptors and buffer.
1419    pub fn split(
1420        self,
1421    ) -> (
1422        DmaAlignedMut<'static, [DmaDescriptor]>,
1423        DmaAlignedMut<'static, [u8]>,
1424    ) {
1425        (self.descriptors, self.buffer)
1426    }
1427
1428    /// Push the buffer with the given data before DMA transfer starts.
1429    ///
1430    /// It's expected to pre-fill at least enough data to fill the first two descriptors' buffers.
1431    /// The more data is pre-filled, the more head-room is left to push more data.
1432    pub fn push(&mut self, data: &[u8]) -> usize {
1433        self.push_with(|buf| {
1434            let len = buf.len().min(data.len());
1435            buf[..len].copy_from_slice(&data[..len]);
1436            len
1437        })
1438    }
1439
1440    /// Push the buffer with the given data before DMA transfer starts.
1441    ///
1442    /// It's expected to pre-fill at least enough data to fill the first two descriptors' buffers.
1443    /// The more data is pre-filled, the more head-room is left to push more data.
1444    ///
1445    /// Returns the number of bytes filled.
1446    pub fn push_with(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> usize {
1447        let start = self.pre_filled.unwrap_or(0);
1448        let bytes_pushed = f(&mut self.buffer[start..]);
1449        self.pre_filled = Some(start + bytes_pushed);
1450        bytes_pushed
1451    }
1452
1453    fn setup_view_state(&mut self) {
1454        let pre_filled = self.pre_filled.unwrap_or(self.buffer.len());
1455        let (idx, offset) = mark_tx_stream_descriptors_ready(&mut self.descriptors, pre_filled);
1456        self.view_descriptor_idx = idx;
1457        self.view_descriptor_offset = offset;
1458        #[cfg(soc_internal_memory_cached)]
1459        if pre_filled != 0 {
1460            unsafe {
1461                crate::soc::cache_writeback_addr(self.buffer.as_ptr() as u32, pre_filled as u32);
1462            }
1463        }
1464    }
1465}
1466
1467/// Marks descriptors containing data that should be transmitted when the DMA
1468/// channel starts.
1469fn mark_tx_stream_descriptors_ready(
1470    descriptors: &mut DmaAlignedMut<'_, [DmaDescriptor]>,
1471    bytes_pushed: usize,
1472) -> (usize, usize) {
1473    if bytes_pushed == 0 {
1474        return (0, 0);
1475    }
1476
1477    #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1478    descriptors.invalidate();
1479
1480    let num = descriptors.len();
1481    let mut bytes_filled = 0;
1482    let mut cursor = (0, 0);
1483
1484    for d in 0..num {
1485        let remaining = bytes_pushed - bytes_filled;
1486        let size = descriptors[d].size();
1487
1488        if remaining == 0 {
1489            terminate_tx_stream_at(descriptors, d);
1490            cursor = (d, 0);
1491            break;
1492        }
1493
1494        if remaining < size {
1495            if d == 0 {
1496                // The transfer needs at least one descriptor; send the partial chunk and
1497                // continue filling from the next one.
1498                descriptors[d].set_owner(Owner::Dma);
1499                descriptors[d].set_length(remaining);
1500                descriptors[d].set_suc_eof(true);
1501                if num > 1 {
1502                    terminate_tx_stream_at(descriptors, 1);
1503                    cursor = (1, 0);
1504                } else {
1505                    descriptors[d].next = null_mut();
1506                }
1507            } else {
1508                terminate_tx_stream_at(descriptors, d);
1509                cursor = (d, remaining);
1510            }
1511            break;
1512        }
1513
1514        bytes_filled += size;
1515        descriptors[d].set_owner(Owner::Dma);
1516        descriptors[d].set_length(size);
1517        descriptors[d].set_suc_eof(true);
1518    }
1519
1520    #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1521    descriptors.writeback();
1522
1523    cursor
1524}
1525
1526fn terminate_tx_stream_at(descriptors: &mut DmaAlignedMut<'_, [DmaDescriptor]>, start: usize) {
1527    if start > 0 {
1528        descriptors[start - 1].next = null_mut();
1529    }
1530    for desc in descriptors.iter_mut().skip(start) {
1531        desc.set_owner(Owner::Cpu);
1532    }
1533}
1534
1535fn advance_tx_stream_descriptors(
1536    descriptors: &mut DmaAlignedMut<'_, [DmaDescriptor]>,
1537    descriptor_idx: &mut usize,
1538    descriptor_offset: &mut usize,
1539    bytes_pushed: usize,
1540) {
1541    if bytes_pushed == 0 {
1542        return;
1543    }
1544
1545    let mut bytes_filled = 0;
1546    let num_descriptors = descriptors.len();
1547
1548    #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1549    descriptors.invalidate();
1550
1551    for i in 0..num_descriptors {
1552        let d = (*descriptor_idx + i) % num_descriptors;
1553        let desc = &mut descriptors[d];
1554        let bytes_in_d = desc.size() - *descriptor_offset;
1555        if bytes_in_d + bytes_filled > bytes_pushed {
1556            *descriptor_idx = d;
1557            *descriptor_offset = *descriptor_offset + bytes_pushed - bytes_filled;
1558            #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1559            descriptors.writeback();
1560            return;
1561        }
1562        bytes_filled += bytes_in_d;
1563        *descriptor_offset = 0;
1564
1565        // Put the current descriptor at the end of the list
1566        desc.set_owner(Owner::Dma);
1567        desc.set_length(desc.size());
1568        desc.set_suc_eof(true);
1569        let p = d.checked_sub(1).unwrap_or(num_descriptors - 1);
1570        if p != d {
1571            let [prev, desc] = descriptors.get_disjoint_mut([p, d]).unwrap();
1572            desc.next = null_mut();
1573            prev.next = desc;
1574        }
1575    }
1576
1577    #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1578    descriptors.writeback();
1579}
1580
1581unsafe impl DmaTxBuffer for DmaTxStreamBuf {
1582    type View = DmaTxStreamBufView;
1583    type Final = Self;
1584
1585    fn prepare(&mut self) -> Preparation {
1586        // Link up all the descriptors (but not in a circle).
1587        let mut next = null_mut();
1588        for desc in self.descriptors.iter_mut().rev() {
1589            desc.next = next;
1590            desc.set_owner(Owner::Dma);
1591            next = desc;
1592        }
1593        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1594        self.descriptors.writeback();
1595
1596        self.setup_view_state();
1597
1598        Preparation {
1599            start: self.descriptors.as_mut_ptr(),
1600            #[cfg(dma_can_access_psram)]
1601            accesses_psram: false,
1602            burst_transfer: self.burst,
1603
1604            // Whilst we give ownership of the descriptors the DMA, the correctness of this buffer
1605            // implementation doesn't rely on the DMA checking for descriptor ownership.
1606            // No descriptor is added back to the end of the stream before it's ready for the DMA
1607            // to consume it.
1608            check_owner: None,
1609            auto_write_back: true,
1610        }
1611    }
1612
1613    fn into_view(self) -> Self::View {
1614        DmaTxStreamBufView {
1615            descriptor_idx: self.view_descriptor_idx,
1616            descriptor_offset: self.view_descriptor_offset,
1617            buf: self,
1618        }
1619    }
1620
1621    fn from_view(view: Self::View) -> Self {
1622        let DmaTxStreamBufView {
1623            mut buf,
1624            descriptor_idx,
1625            descriptor_offset,
1626        } = view;
1627        buf.view_descriptor_idx = descriptor_idx;
1628        buf.view_descriptor_offset = descriptor_offset;
1629        buf
1630    }
1631}
1632
1633/// A view into a [DmaTxStreamBuf]
1634pub struct DmaTxStreamBufView {
1635    buf: DmaTxStreamBuf,
1636    descriptor_idx: usize,
1637    descriptor_offset: usize,
1638}
1639
1640impl DmaTxStreamBufView {
1641    /// Returns the number of bytes available for writing.
1642    pub fn available_bytes(&mut self) -> usize {
1643        #[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
1644        self.buf.descriptors.invalidate();
1645
1646        let (tail, head) = self.buf.descriptors.split_at(self.descriptor_idx);
1647        head.iter()
1648            .chain(tail)
1649            .take_while(|d| d.owner() == Owner::Cpu)
1650            .map(|d| d.size())
1651            .sum::<usize>()
1652            .saturating_sub(self.descriptor_offset)
1653    }
1654
1655    fn write_position(&self) -> usize {
1656        let desc = &self.buf.descriptors[self.descriptor_idx];
1657        desc.buffer
1658            .addr()
1659            .wrapping_sub(self.buf.buffer.as_ptr().addr())
1660            + self.descriptor_offset
1661    }
1662
1663    /// Pushes a buffer into the stream buffer.
1664    /// Returns the number of bytes pushed.
1665    pub fn push_with(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> usize {
1666        let dma_start = self.write_position();
1667        let dma_end = dma_start
1668            .saturating_add(self.available_bytes())
1669            .min(self.buf.buffer.len())
1670            .max(dma_start);
1671        let bytes_pushed = f(&mut self.buf.buffer[dma_start..dma_end]).min(dma_end - dma_start);
1672        #[cfg(soc_internal_memory_cached)]
1673        if bytes_pushed != 0 {
1674            unsafe {
1675                crate::soc::cache_writeback_addr(
1676                    self.buf.buffer.as_ptr().add(dma_start) as u32,
1677                    bytes_pushed as u32,
1678                );
1679            }
1680        }
1681
1682        self.advance(bytes_pushed);
1683        bytes_pushed
1684    }
1685
1686    /// Advances the first `n` bytes from the available data
1687    pub fn advance(&mut self, bytes_pushed: usize) {
1688        advance_tx_stream_descriptors(
1689            &mut self.buf.descriptors,
1690            &mut self.descriptor_idx,
1691            &mut self.descriptor_offset,
1692            bytes_pushed,
1693        );
1694    }
1695
1696    /// Pushes a buffer into the stream buffer.
1697    /// Returns the number of bytes pushed.
1698    pub fn push(&mut self, data: &[u8]) -> usize {
1699        let total_len = data.len();
1700        let mut remaining = data;
1701
1702        while !remaining.is_empty() && self.available_bytes() > 0 {
1703            let written = self.push_with(|buffer| {
1704                let len = usize::min(buffer.len(), remaining.len());
1705                buffer[..len].copy_from_slice(&remaining[..len]);
1706                len
1707            });
1708            if written == 0 {
1709                break;
1710            }
1711            remaining = &remaining[written..];
1712        }
1713
1714        total_len - remaining.len()
1715    }
1716}
1717
1718static mut EMPTY: InternalMemory<[DmaDescriptor; 1]> = InternalMemory::new([DmaDescriptor::EMPTY]);
1719
1720/// An empty buffer that can be used when you don't need to transfer any data.
1721pub struct EmptyBuf;
1722
1723unsafe impl DmaTxBuffer for EmptyBuf {
1724    type View = EmptyBuf;
1725    type Final = EmptyBuf;
1726
1727    fn prepare(&mut self) -> Preparation {
1728        #[cfg(soc_internal_memory_cached)]
1729        #[allow(static_mut_refs)]
1730        unsafe {
1731            EMPTY.get_mut().writeback();
1732        }
1733
1734        Preparation {
1735            start: (&raw mut EMPTY).cast(),
1736            #[cfg(dma_can_access_psram)]
1737            accesses_psram: false,
1738            burst_transfer: BurstConfig::default(),
1739
1740            // As we don't give ownership of the descriptor to the DMA, it's important that the DMA
1741            // channel does *NOT* check for ownership, otherwise the channel will return an error.
1742            check_owner: Some(false),
1743
1744            // The DMA should not write back to the descriptor as it is shared.
1745            auto_write_back: false,
1746        }
1747    }
1748
1749    fn into_view(self) -> EmptyBuf {
1750        self
1751    }
1752
1753    fn from_view(view: Self::View) -> Self {
1754        view
1755    }
1756}
1757
1758unsafe impl DmaRxBuffer for EmptyBuf {
1759    type View = EmptyBuf;
1760    type Final = EmptyBuf;
1761
1762    fn prepare(&mut self) -> Preparation {
1763        #[cfg(soc_internal_memory_cached)]
1764        #[allow(static_mut_refs)]
1765        unsafe {
1766            EMPTY.get_mut().writeback();
1767        }
1768
1769        Preparation {
1770            start: (&raw mut EMPTY).cast(),
1771            #[cfg(dma_can_access_psram)]
1772            accesses_psram: false,
1773            burst_transfer: BurstConfig::default(),
1774
1775            // As we don't give ownership of the descriptor to the DMA, it's important that the DMA
1776            // channel does *NOT* check for ownership, otherwise the channel will return an error.
1777            check_owner: Some(false),
1778            auto_write_back: true,
1779        }
1780    }
1781
1782    fn into_view(self) -> EmptyBuf {
1783        self
1784    }
1785
1786    fn from_view(view: Self::View) -> Self {
1787        view
1788    }
1789}
1790
1791/// DMA Loop Buffer
1792///
1793/// This consists of a single descriptor that points to itself and points to a
1794/// single buffer, resulting in the buffer being transmitted over and over
1795/// again, indefinitely.
1796///
1797/// Note: A DMA descriptor is 12 bytes. If your buffer is significantly shorter
1798/// than this, the DMA channel will spend more time reading the descriptor than
1799/// it does reading the buffer, which may leave it unable to keep up with the
1800/// bandwidth requirements of some peripherals at high frequencies.
1801pub struct DmaLoopBuf {
1802    descriptor: DmaAlignedMut<'static, [DmaDescriptor]>,
1803    buffer: DmaAlignedMut<'static, [u8]>,
1804}
1805
1806impl DmaLoopBuf {
1807    /// Create a new [DmaLoopBuf].
1808    pub fn new(
1809        mut descriptors: DmaAlignedMut<'static, [DmaDescriptor]>,
1810        mut buffer: DmaAlignedMut<'static, [u8]>,
1811    ) -> Result<DmaLoopBuf, DmaBufError> {
1812        if buffer.len() > BurstConfig::default().max_chunk_size_for(&buffer, TransferDirection::Out)
1813        {
1814            return Err(DmaBufError::InsufficientDescriptors);
1815        }
1816
1817        descriptors[0].set_owner(Owner::Dma); // Doesn't matter
1818        descriptors[0].set_suc_eof(false);
1819        descriptors[0].set_length(buffer.len());
1820        descriptors[0].set_size(buffer.len());
1821        descriptors[0].buffer = buffer.as_mut_ptr();
1822        descriptors[0].next = descriptors.as_mut_ptr();
1823
1824        Ok(Self {
1825            descriptor: descriptors,
1826            buffer,
1827        })
1828    }
1829
1830    /// Consume the buf, returning the descriptor and buffer.
1831    pub fn split(
1832        self,
1833    ) -> (
1834        DmaAlignedMut<'static, [DmaDescriptor]>,
1835        DmaAlignedMut<'static, [u8]>,
1836    ) {
1837        (self.descriptor, self.buffer)
1838    }
1839}
1840
1841unsafe impl DmaTxBuffer for DmaLoopBuf {
1842    type View = DmaLoopBuf;
1843    type Final = DmaLoopBuf;
1844
1845    fn prepare(&mut self) -> Preparation {
1846        Preparation {
1847            start: self.descriptor.as_mut_ptr(),
1848            #[cfg(dma_can_access_psram)]
1849            accesses_psram: false,
1850            burst_transfer: BurstConfig::default(),
1851            // The DMA must not check the owner bit, as it is never set.
1852            check_owner: Some(false),
1853
1854            // Doesn't matter either way but it is set to true for ESP32 SPI_DMA compatibility.
1855            auto_write_back: false,
1856        }
1857    }
1858
1859    fn into_view(self) -> Self::View {
1860        self
1861    }
1862
1863    fn from_view(view: Self::View) -> Self {
1864        view
1865    }
1866}
1867
1868impl Deref for DmaLoopBuf {
1869    type Target = [u8];
1870
1871    fn deref(&self) -> &Self::Target {
1872        &self.buffer
1873    }
1874}
1875
1876impl DerefMut for DmaLoopBuf {
1877    fn deref_mut(&mut self) -> &mut Self::Target {
1878        &mut self.buffer
1879    }
1880}
1881
1882/// A Preparation that masks itself as a DMA buffer.
1883///
1884/// Fow low level use, where none of the pre-made buffers really fit.
1885///
1886/// This type likely never should be visible outside of esp-hal.
1887pub(crate) struct NoBuffer(pub(crate) Preparation);
1888impl NoBuffer {
1889    fn prep(&self) -> Preparation {
1890        Preparation {
1891            start: self.0.start,
1892            #[cfg(dma_can_access_psram)]
1893            accesses_psram: self.0.accesses_psram,
1894            burst_transfer: self.0.burst_transfer,
1895            check_owner: self.0.check_owner,
1896            auto_write_back: self.0.auto_write_back,
1897        }
1898    }
1899}
1900unsafe impl DmaTxBuffer for NoBuffer {
1901    type View = ();
1902    type Final = ();
1903
1904    fn prepare(&mut self) -> Preparation {
1905        self.prep()
1906    }
1907
1908    fn into_view(self) -> Self::View {}
1909    fn from_view(_view: Self::View) {}
1910}
1911unsafe impl DmaRxBuffer for NoBuffer {
1912    type View = ();
1913    type Final = ();
1914
1915    fn prepare(&mut self) -> Preparation {
1916        self.prep()
1917    }
1918
1919    fn into_view(self) -> Self::View {}
1920    fn from_view(_view: Self::View) {}
1921}
1922
1923/// Prepares data unsafely to be transmitted via DMA.
1924///
1925/// `block_size` is the requirement imposed by the peripheral that receives the data. It
1926/// ensures that the DMA will not try to copy a partial block, which would cause the RX DMA (that
1927/// moves results back into RAM) to never complete.
1928///
1929/// The function returns the DMA buffer, and the number of bytes that will be transferred.
1930///
1931/// # Safety
1932///
1933/// The caller must keep all its descriptors and the buffers they
1934/// point to valid while the buffer is being transferred.
1935#[cfg_attr(not(any(aes_supports_dma, spi_master_supports_dma)), expect(unused))]
1936pub(crate) unsafe fn prepare_for_tx(
1937    descriptors: &mut [DmaDescriptor],
1938    mut data: NonNull<[u8]>,
1939    block_size: usize,
1940) -> Result<(NoBuffer, usize), DmaError> {
1941    let alignment =
1942        BurstConfig::DEFAULT.min_alignment(unsafe { data.as_ref() }, TransferDirection::Out);
1943
1944    if !data.addr().get().is_multiple_of(alignment) {
1945        // ESP32 has word alignment requirement on the TX descriptors, too.
1946        return Err(DmaError::InvalidAlignment(DmaAlignmentError::Address));
1947    }
1948
1949    // Whichever is stricter, data location or peripheral requirements.
1950    //
1951    // This ensures that the RX DMA, if used, can transfer the returned number of bytes using at
1952    // most N+2 descriptors. While the hardware doesn't require this on the TX DMA side, (the TX DMA
1953    // can, except on the ESP32, transfer any amount of data), it makes usage MUCH simpler.
1954    let alignment = alignment.max(block_size);
1955    let chunk_size = 4096 - alignment;
1956
1957    let data_len = data.len().min(chunk_size * descriptors.len());
1958
1959    cfg_select! {
1960        dma_can_access_psram => {
1961            let data_addr = data.addr().get();
1962            let data_in_psram = crate::psram::psram_range().contains(&data_addr);
1963
1964            // Make sure input data is in PSRAM instead of cache
1965            if data_in_psram || cfg!(soc_internal_memory_cached) {
1966                unsafe { crate::soc::cache_writeback_addr(data_addr as u32, data_len as u32) };
1967            }
1968        }
1969        soc_internal_memory_cached => {
1970            unsafe { crate::soc::cache_writeback_addr(data.addr().get() as u32, data_len as u32) };
1971        }
1972        _ => {}
1973    }
1974
1975    let descriptors = unsafe { DmaAlignedMut::new_unchecked(descriptors) };
1976    let mut descriptors = unwrap!(DescriptorSet::new(descriptors));
1977    // TODO: it would be best if this function returned the amount of data that could be linked
1978    // up.
1979    unwrap!(descriptors.link_with_buffer(unsafe { data.as_mut() }, chunk_size));
1980    unwrap!(descriptors.set_tx_length(data_len, chunk_size));
1981
1982    for desc in descriptors.linked_iter_mut() {
1983        desc.reset_for_tx(desc.next.is_null());
1984    }
1985
1986    #[cfg(soc_internal_memory_cached)]
1987    descriptors.descriptors.writeback();
1988
1989    Ok((
1990        NoBuffer(Preparation {
1991            start: descriptors.head(),
1992            burst_transfer: BurstConfig::DEFAULT,
1993            check_owner: None,
1994            auto_write_back: false,
1995            #[cfg(dma_can_access_psram)]
1996            accesses_psram: data_in_psram,
1997        }),
1998        data_len,
1999    ))
2000}
2001
2002/// Prepare buffers to receive data from DMA.
2003///
2004/// The function returns the DMA buffer, and the number of bytes that will be transferred.
2005///
2006/// # Safety
2007///
2008/// The caller must keep all its descriptors and the buffers they
2009/// point to valid while the buffer is being transferred.
2010#[cfg_attr(not(any(aes_supports_dma, spi_master_supports_dma)), expect(unused))]
2011pub(crate) unsafe fn prepare_for_rx(
2012    descriptors: &mut [DmaDescriptor],
2013    #[cfg(dma_can_access_psram)] align_buffers: &mut [Option<ManualWritebackBuffer>; 2],
2014    mut data: NonNull<[u8]>,
2015) -> (NoBuffer, usize) {
2016    let chunk_size =
2017        BurstConfig::DEFAULT.max_chunk_size_for(unsafe { data.as_ref() }, TransferDirection::In);
2018
2019    // The data we have to process may not be appropriate for the DMA:
2020    // - it may be improperly aligned for PSRAM
2021    // - it may not have a length that is a multiple of the external memory block size
2022
2023    cfg_select! {
2024        dma_can_access_psram => {
2025            let data_addr = data.addr().get();
2026            let data_in_psram = crate::psram::psram_range().contains(&data_addr);
2027        }
2028        _ => {
2029            let data_in_psram = false;
2030        }
2031    }
2032
2033    let descriptors = unsafe { DmaAlignedMut::new_unchecked(descriptors) };
2034    let mut descriptors = unwrap!(DescriptorSet::new(descriptors));
2035    let data_len = if data_in_psram {
2036        cfg_select! {
2037            dma_can_access_psram => {
2038                // This could use a better API, but right now we'll have to build the descriptor
2039                // list by hand.
2040                let consumed_bytes =
2041                    build_descriptor_list_for_psram(&mut descriptors, align_buffers, data);
2042
2043                // Invalidate data written by the DMA. As this likely affects more data than we
2044                // touched, write back first.
2045                unsafe {
2046                    crate::soc::cache_writeback_addr(data_addr as u32, consumed_bytes as u32);
2047                    crate::soc::cache_invalidate_addr(data_addr as u32, consumed_bytes as u32);
2048                }
2049
2050                consumed_bytes
2051            }
2052            _ => {
2053                unreachable!()
2054            }
2055        }
2056    } else {
2057        // Just set up descriptors as usual
2058        let data_len = data.len();
2059        unwrap!(descriptors.link_with_buffer(unsafe { data.as_mut() }, chunk_size));
2060        unwrap!(descriptors.set_tx_length(data_len, chunk_size));
2061
2062        #[cfg(soc_internal_memory_cached)]
2063        // Invalidate data written by the DMA. As this likely affects more data than we touched,
2064        // write back first.
2065        unsafe {
2066            crate::soc::cache_writeback_addr(data.addr().get() as u32, data_len as u32);
2067            crate::soc::cache_invalidate_addr(data.addr().get() as u32, data_len as u32);
2068        }
2069
2070        data_len
2071    };
2072
2073    for desc in descriptors.linked_iter_mut() {
2074        desc.reset_for_rx();
2075    }
2076
2077    #[cfg(soc_internal_memory_cached)]
2078    descriptors.descriptors.writeback();
2079
2080    (
2081        NoBuffer(Preparation {
2082            start: descriptors.head(),
2083            burst_transfer: BurstConfig::DEFAULT,
2084            check_owner: None,
2085            auto_write_back: true,
2086            #[cfg(dma_can_access_psram)]
2087            accesses_psram: data_in_psram,
2088        }),
2089        data_len,
2090    )
2091}
2092
2093#[cfg(dma_can_access_psram)]
2094fn build_descriptor_list_for_psram(
2095    descriptors: &mut DescriptorSet<'_>,
2096    copy_buffers: &mut [Option<ManualWritebackBuffer>; 2],
2097    data: NonNull<[u8]>,
2098) -> usize {
2099    let data_len = data.len();
2100    let data_addr = data.addr().get();
2101
2102    let min_alignment = ExternalBurstConfig::DEFAULT.min_psram_alignment(TransferDirection::In);
2103    let chunk_size = 4096 - min_alignment;
2104
2105    let mut desciptor_iter = DescriptorChainingIter::new(&mut descriptors.descriptors);
2106    let mut copy_buffer_iter = copy_buffers.iter_mut();
2107
2108    // MIN_LAST_DMA_LEN could make this really annoying, so we're just allocating a bit larger
2109    // buffer and shove edge cases into a single one. If we have >24 bytes on the S2, the 2-buffer
2110    // alignment algo works fine as one of them can steal 16 bytes, the other will have
2111    // MIN_LAST_DMA_LEN data to work with.
2112    let has_aligned_data = data_len > BUF_LEN;
2113
2114    // Calculate byte offset to the start of the buffer
2115    let offset = data_addr % min_alignment;
2116    let head_to_copy = min_alignment - offset;
2117    let head_to_copy = if !has_aligned_data {
2118        BUF_LEN
2119    } else if head_to_copy > 0 && head_to_copy < MIN_LAST_DMA_LEN {
2120        head_to_copy + min_alignment
2121    } else {
2122        head_to_copy
2123    };
2124    let head_to_copy = head_to_copy.min(data_len);
2125
2126    // Calculate last unaligned part
2127    let tail_to_copy = (data_len - head_to_copy) % min_alignment;
2128    let tail_to_copy = if tail_to_copy > 0 && tail_to_copy < MIN_LAST_DMA_LEN {
2129        tail_to_copy + min_alignment
2130    } else {
2131        tail_to_copy
2132    };
2133
2134    let mut consumed = 0;
2135
2136    // Align beginning
2137    if head_to_copy > 0 {
2138        let copy_buffer = unwrap!(copy_buffer_iter.next());
2139        let buffer =
2140            copy_buffer.insert(ManualWritebackBuffer::new(get_range(data, 0..head_to_copy)));
2141        buffer.prepare_for_dma();
2142
2143        let Some(descriptor) = desciptor_iter.next() else {
2144            return consumed;
2145        };
2146        descriptor.set_size(head_to_copy);
2147        descriptor.buffer = buffer.mut_buffer_ptr();
2148        consumed += head_to_copy;
2149    };
2150
2151    // Chain up descriptors for the main aligned data part.
2152    let mut aligned_data = get_range(data, head_to_copy..data.len() - tail_to_copy);
2153    while !aligned_data.is_empty() {
2154        let Some(descriptor) = desciptor_iter.next() else {
2155            return consumed;
2156        };
2157        let chunk = aligned_data.len().min(chunk_size);
2158
2159        descriptor.set_size(chunk);
2160        descriptor.buffer = aligned_data.cast::<u8>().as_ptr();
2161        consumed += chunk;
2162        aligned_data = get_range(aligned_data, chunk..aligned_data.len());
2163    }
2164
2165    // Align end
2166    if tail_to_copy > 0 {
2167        let copy_buffer = unwrap!(copy_buffer_iter.next());
2168        let buffer = copy_buffer.insert(ManualWritebackBuffer::new(get_range(
2169            data,
2170            data.len() - tail_to_copy..data.len(),
2171        )));
2172        buffer.prepare_for_dma();
2173
2174        let Some(descriptor) = desciptor_iter.next() else {
2175            return consumed;
2176        };
2177        descriptor.set_size(tail_to_copy);
2178        descriptor.buffer = buffer.mut_buffer_ptr();
2179        consumed += tail_to_copy;
2180    }
2181
2182    consumed
2183}
2184
2185#[cfg(dma_can_access_psram)]
2186fn get_range(ptr: NonNull<[u8]>, range: Range<usize>) -> NonNull<[u8]> {
2187    let len = range.end - range.start;
2188    NonNull::slice_from_raw_parts(unsafe { ptr.cast().byte_add(range.start) }, len)
2189}
2190
2191#[cfg(dma_can_access_psram)]
2192struct DescriptorChainingIter<'a> {
2193    /// index of the next element to emit
2194    index: usize,
2195    descriptors: &'a mut [DmaDescriptor],
2196}
2197#[cfg(dma_can_access_psram)]
2198impl<'a> DescriptorChainingIter<'a> {
2199    fn new(descriptors: &'a mut [DmaDescriptor]) -> Self {
2200        Self {
2201            descriptors,
2202            index: 0,
2203        }
2204    }
2205
2206    fn next(&mut self) -> Option<&'_ mut DmaDescriptor> {
2207        if self.index == 0 {
2208            self.index += 1;
2209            self.descriptors.get_mut(0)
2210        } else if self.index < self.descriptors.len() {
2211            let index = self.index;
2212            self.index += 1;
2213
2214            // Grab a pointer to the current descriptor.
2215            let ptr = &raw mut self.descriptors[index];
2216
2217            // Link the descriptor to the previous one.
2218            self.descriptors[index - 1].next = ptr;
2219
2220            // Reborrow the pointer so that it doesn't get invalidated by our continued use of the
2221            // descriptor reference.
2222            Some(unsafe { &mut *ptr })
2223        } else {
2224            None
2225        }
2226    }
2227}
2228
2229#[cfg(dma_can_access_psram)]
2230const MIN_LAST_DMA_LEN: usize = if cfg!(esp32s2) { 5 } else { 1 };
2231#[cfg(dma_can_access_psram)]
2232const BUF_LEN: usize = 16 + 2 * (MIN_LAST_DMA_LEN - 1); // 2x makes aligning short buffers simpler
2233
2234/// PSRAM helper. DMA can write data of any alignment into this buffer, and it can be written by
2235/// the CPU back to PSRAM.
2236#[cfg(dma_can_access_psram)]
2237pub(crate) struct ManualWritebackBuffer {
2238    buffer: InternalMemory<MaybeUninit<[u8; BUF_LEN]>>,
2239    dst_address: NonNull<u8>,
2240    n_bytes: u8,
2241}
2242
2243#[cfg(dma_can_access_psram)]
2244impl ManualWritebackBuffer {
2245    pub fn new(ptr: NonNull<[u8]>) -> Self {
2246        assert!(ptr.len() <= BUF_LEN);
2247        Self {
2248            buffer: InternalMemory::new(MaybeUninit::uninit()),
2249            dst_address: ptr.cast(),
2250            n_bytes: ptr.len() as u8,
2251        }
2252    }
2253
2254    pub fn prepare_for_dma(&mut self) {
2255        // Ensure our cache line is not dirty. A dirty cacheline
2256        // evicted during DMA operation can clobber received data.
2257        #[cfg(soc_internal_memory_cached)]
2258        self.buffer.get_mut().invalidate();
2259    }
2260
2261    pub fn write_back(&mut self) {
2262        // The DMA wrote its data directly to memory, bypassing the CPU cache.
2263        // Invalidate the cache lines covering the alignment buffer so the CPU
2264        // reads the fresh DMA data rather than the stale zeros from new().
2265        #[cfg(soc_internal_memory_cached)]
2266        self.buffer.get_mut().invalidate();
2267
2268        let src = self.mut_buffer_ptr().cast_const();
2269        unsafe {
2270            self.dst_address
2271                .as_ptr()
2272                .copy_from(src, self.n_bytes as usize);
2273        }
2274    }
2275
2276    pub fn mut_buffer_ptr(&mut self) -> *mut u8 {
2277        self.buffer.get_mut().as_mut_ptr().cast::<u8>()
2278    }
2279}