Skip to main content

esp_hal/dma/engine/
mod.rs

1use enumset::{EnumSet, EnumSetType};
2
3use crate::{
4    asynch::AtomicWaker,
5    dma::{BurstConfig, DmaRxInterrupt, DmaTxInterrupt},
6    interrupt::InterruptHandler,
7    peripherals::Interrupt,
8    private::{Internal, Sealed},
9    system::PeripheralGuard,
10};
11
12for_each_dma_engine! {
13    ("AHB_GDMA") => {
14        mod gdma;
15        pub use gdma::*;
16    };
17    ("AXI_GDMA") => {
18        mod axi_gdma;
19        pub use axi_gdma::*;
20    };
21    ("COPY_DMA") => {
22        mod copy;
23        pub use copy::*;
24    };
25    ("CRYPTO_DMA") => {
26        mod crypto;
27        pub use crypto::*;
28    };
29    ("I2S_DMA") => {
30        mod i2s;
31        pub use i2s::*;
32    };
33    ("SPI_DMA") => {
34        mod spi;
35        pub use spi::*;
36    };
37}
38
39/// Implemented by peripheral singletons that can be used with a DMA engine.
40pub trait DmaEligiblePeripheral<D: DmaChannel> {
41    /// Returns the `DmaPeripheral` ID for runtime compatibility checks.
42    fn dma_peripheral(&self) -> DmaPeripheral;
43}
44
45for_each_peripheral! {
46    (dma_eligible $(( $peri:ident, $name:ident, $id:literal, $any_ch:ident )),*) => {
47        /// DMA-eligible peripheral selector values; values are engine-local (matching hardware where applicable).
48        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
49        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
50        #[doc(hidden)]
51        pub struct DmaPeripheral(pub(crate) u8);
52        impl DmaPeripheral {
53            $(
54                #[doc = concat!("DMA accesses ", stringify!($name))]
55                pub const $peri: Self = Self($id);
56            )*
57        }
58
59        $(
60            impl<'d> DmaEligiblePeripheral<$any_ch<'d>> for crate::peripherals::$peri<'d> {
61                fn dma_peripheral(&self) -> DmaPeripheral {
62                    DmaPeripheral::$peri
63                }
64            }
65        )*
66    };
67}
68
69#[doc(hidden)]
70pub trait RegisterAccess: Sealed {
71    #[allow(private_interfaces)]
72    fn enable(&self) -> Option<PeripheralGuard>;
73
74    /// Reset the state machine of the channel and FIFO pointer.
75    fn reset(&self);
76
77    /// Enable/Disable INCR burst transfer for channel reading
78    /// accessing data in internal RAM.
79    fn set_burst_mode(&self, burst_mode: BurstConfig);
80
81    /// Enable/Disable burst transfer for channel reading
82    /// descriptors in internal RAM.
83    fn set_descr_burst_mode(&self, burst_mode: bool);
84
85    /// The priority of the channel. The larger the value, the higher the
86    /// priority.
87    #[cfg(dma_max_priority_is_set)]
88    fn set_priority(&self, priority: crate::dma::DmaPriority);
89
90    /// Select a peripheral for the channel.
91    fn set_peripheral(&self, _peripheral: u8) {}
92
93    /// Set the address of the first descriptor.
94    fn set_link_addr(&self, address: u32);
95
96    /// Enable the channel for data transfer.
97    fn start(&self);
98
99    /// Stop the channel from transferring data.
100    fn stop(&self);
101
102    /// Mount a new descriptor.
103    fn restart(&self);
104
105    /// Configure the bit to enable checking the owner attribute of the
106    /// descriptor.
107    fn set_check_owner(&self, check_owner: Option<bool>);
108
109    #[cfg(dma_ext_mem_configurable_block_size)]
110    fn set_ext_mem_block_size(&self, size: crate::dma::DmaExtMemBKSize);
111
112    #[cfg(dma_can_access_psram)]
113    fn can_access_psram(&self) -> bool;
114
115    fn compatible_peripherals(&self) -> &[u8];
116
117    fn runtime_ensure_compatible(&self, peripheral: DmaPeripheral) {
118        let peripherals = self.compatible_peripherals();
119        assert!(
120            peripherals.contains(&peripheral.0),
121            "This DMA channel is not compatible with peripheral id {}",
122            peripheral.0
123        );
124    }
125}
126
127#[doc(hidden)]
128pub trait RxRegisterAccess: RegisterAccess {
129    #[cfg(dma_supports_mem2mem)]
130    fn set_mem2mem_mode(&self, value: bool);
131
132    fn peripheral_interrupt(&self) -> Option<Interrupt>;
133    fn async_handler(&self) -> Option<InterruptHandler>;
134}
135
136#[doc(hidden)]
137pub trait TxRegisterAccess: RegisterAccess {
138    /// Returns whether the DMA's FIFO is empty.
139    fn is_fifo_empty(&self) -> bool;
140
141    /// Enable/disable outlink-writeback
142    fn set_auto_write_back(&self, enable: bool);
143
144    /// Outlink descriptor address when EOF occurs of Tx channel.
145    fn last_dscr_address(&self) -> usize;
146
147    fn peripheral_interrupt(&self) -> Option<Interrupt>;
148    fn async_handler(&self) -> Option<InterruptHandler>;
149}
150
151#[doc(hidden)]
152pub trait InterruptAccess<T: EnumSetType>: Sealed {
153    fn listen(&self, interrupts: impl Into<EnumSet<T>>) {
154        self.enable_listen(interrupts.into(), true)
155    }
156    fn unlisten(&self, interrupts: impl Into<EnumSet<T>>) {
157        self.enable_listen(interrupts.into(), false)
158    }
159
160    fn clear_all(&self) {
161        self.clear(EnumSet::all());
162    }
163
164    fn enable_listen(&self, interrupts: EnumSet<T>, enable: bool);
165    fn is_listening(&self) -> EnumSet<T>;
166    fn clear(&self, interrupts: impl Into<EnumSet<T>>);
167    fn pending_interrupts(&self) -> EnumSet<T>;
168    fn waker(&self) -> &'static AtomicWaker;
169
170    fn is_async(&self) -> bool;
171    fn set_async(&self, is_async: bool);
172}
173
174#[instability::unstable]
175pub trait DmaRxChannel: RxRegisterAccess + InterruptAccess<DmaRxInterrupt> {}
176
177#[instability::unstable]
178pub trait DmaTxChannel: TxRegisterAccess + InterruptAccess<DmaTxInterrupt> {}
179
180/// A description of a DMA Channel.
181pub trait DmaChannel: Sized + crate::private::Sealed {
182    /// A description of the RX half of a DMA Channel.
183    type Rx: DmaRxChannel + From<Self>;
184
185    /// A description of the TX half of a DMA Channel.
186    type Tx: DmaTxChannel + From<Self>;
187
188    /// Splits the DMA channel into its RX and TX halves.
189    #[cfg(any(esp32c5, esp32c6, esp32h2, esp32s3))] // TODO relax this to allow splitting on all chips
190    fn split(self) -> (Self::Rx, Self::Tx) {
191        // This function is exposed safely on chips that have separate IN and OUT
192        // interrupt handlers.
193        // TODO: this includes the P4 as well.
194        unsafe { self.split_internal(Internal) }
195    }
196
197    /// Splits the DMA channel into its RX and TX halves.
198    ///
199    /// # Safety
200    ///
201    /// This function must only be used if the separate halves are used by the
202    /// same peripheral.
203    unsafe fn split_internal(self, _: Internal) -> (Self::Rx, Self::Tx);
204}
205
206#[doc(hidden)]
207pub trait DmaChannelExt: DmaChannel {
208    fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt>;
209    fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt>;
210}
211
212macro_rules! impl_channel_common {
213    ($peri:ident, $instance:ident) => {
214        paste::paste! {
215            impl<'d> DmaChannel for $instance<'d> {
216                type Rx = [<$peri RxChannel>]<'d>;
217                type Tx = [<$peri TxChannel>]<'d>;
218
219                unsafe fn split_internal(self, _: $crate::private::Internal) -> (Self::Rx, Self::Tx) {
220                    unsafe {
221                        (
222                            [<$peri RxChannel>](Self::steal().into()),
223                            [<$peri TxChannel>](Self::steal().into()),
224                        )
225                    }
226                }
227            }
228
229            // Convert concrete channel into erased TX/RX half structs
230            impl<'d> From<$instance<'d>> for [<$peri RxChannel>]<'d> {
231                fn from(this: $instance<'d>) -> [<$peri RxChannel>]<'d> {
232                    [<$peri RxChannel>](this.into())
233                }
234            }
235
236            impl<'d> From<$instance<'d>> for [<$peri TxChannel>]<'d> {
237                fn from(this: $instance<'d>) -> [<$peri TxChannel>]<'d> {
238                    [<$peri TxChannel>](this.into())
239                }
240            }
241
242            impl crate::dma::DmaChannelExt for $instance<'_> {
243                fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt> {
244                    [<$peri RxChannel>]::from(unsafe { Self::steal() })
245                }
246
247                fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt> {
248                    [<$peri TxChannel>]::from(unsafe { Self::steal() })
249                }
250            }
251        }
252    };
253}
254pub(crate) use impl_channel_common;