Skip to main content

esp_hal/dma/engine/
crypto.rs

1use enumset::EnumSet;
2use portable_atomic::{AtomicBool, Ordering};
3
4use crate::{
5    RegisterToggle,
6    asynch::AtomicWaker,
7    dma::{
8        BurstConfig,
9        DmaChannel,
10        DmaExtMemBKSize,
11        DmaPeripheral,
12        DmaRxChannel,
13        DmaRxInterrupt,
14        DmaTxChannel,
15        DmaTxInterrupt,
16        InterruptAccess,
17        RegisterAccess,
18        RxRegisterAccess,
19        TxRegisterAccess,
20        asynch,
21    },
22    interrupt::InterruptHandler,
23    peripherals::{DMA_CRYPTO, Interrupt},
24    system::{Peripheral, PeripheralGuard},
25};
26
27/// Immutable per-channel metadata.
28#[doc(hidden)]
29pub struct ChannelInfo {
30    #[expect(dead_code)]
31    pub(crate) peripheral_interrupt: Interrupt,
32
33    #[expect(dead_code)]
34    pub(crate) async_handler: InterruptHandler,
35
36    /// Peripheral IDs this channel can serve. An empty slice means no runtime check is needed.
37    pub(crate) compatible_peripherals: &'static [u8],
38}
39
40/// Mutable per-channel runtime state (wakers and async-mode flags).
41pub(crate) struct ChannelState {
42    /// Async waker for the TX (out) half of this channel.
43    pub(crate) tx_waker: AtomicWaker,
44
45    /// Async waker for the RX (in) half of this channel.
46    pub(crate) rx_waker: AtomicWaker,
47
48    /// Whether the TX half is currently in async mode.
49    pub(crate) tx_async_flag: portable_atomic::AtomicBool,
50
51    /// Whether the RX half is currently in async mode.
52    pub(crate) rx_async_flag: portable_atomic::AtomicBool,
53}
54
55pub(super) type CryptoRegisterBlock = crate::pac::crypto_dma::RegisterBlock;
56
57/// The RX half of a Crypto DMA channel.
58#[derive(Debug)]
59#[cfg_attr(feature = "defmt", derive(defmt::Format))]
60pub struct CryptoDmaRxChannel<'d>(CryptoDmaChannel<'d>);
61
62impl CryptoDmaRxChannel<'_> {
63    fn regs(&self) -> &CryptoRegisterBlock {
64        self.0.register_block()
65    }
66}
67
68impl crate::private::Sealed for CryptoDmaRxChannel<'_> {}
69impl DmaRxChannel for CryptoDmaRxChannel<'_> {}
70
71/// The TX half of a Crypto DMA channel.
72#[derive(Debug)]
73#[cfg_attr(feature = "defmt", derive(defmt::Format))]
74pub struct CryptoDmaTxChannel<'d>(CryptoDmaChannel<'d>);
75
76impl CryptoDmaTxChannel<'_> {
77    fn regs(&self) -> &CryptoRegisterBlock {
78        self.0.register_block()
79    }
80}
81
82impl crate::private::Sealed for CryptoDmaTxChannel<'_> {}
83impl DmaTxChannel for CryptoDmaTxChannel<'_> {}
84
85impl RegisterAccess for CryptoDmaTxChannel<'_> {
86    #[allow(private_interfaces)]
87    fn enable(&self) -> Option<PeripheralGuard> {
88        Some(PeripheralGuard::new(Peripheral::CryptoDma))
89    }
90
91    fn reset(&self) {
92        self.regs().conf().toggle(|w, bit| {
93            w.out_rst().bit(bit);
94            w.ahbm_rst().bit(bit);
95            w.ahbm_fifo_rst().bit(bit)
96        });
97    }
98
99    fn set_burst_mode(&self, burst_mode: BurstConfig) {
100        self.regs()
101            .conf()
102            .modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled()));
103    }
104
105    fn set_descr_burst_mode(&self, burst_mode: bool) {
106        self.regs()
107            .conf()
108            .modify(|_, w| w.outdscr_burst_en().bit(burst_mode));
109    }
110
111    fn set_peripheral(&self, peripheral: u8) {
112        use esp32s2::crypto_dma::aes_sha_select::SELECT;
113        let sel = match peripheral {
114            p if p == DmaPeripheral::AES.0 => SELECT::Aes,
115            p if p == DmaPeripheral::SHA.0 => SELECT::Sha,
116            _ => unreachable!(),
117        };
118        self.regs()
119            .aes_sha_select()
120            .modify(|_, w| w.select().variant(sel));
121    }
122
123    fn set_link_addr(&self, address: u32) {
124        self.regs()
125            .out_link()
126            .modify(|_, w| unsafe { w.outlink_addr().bits(address) });
127    }
128
129    fn start(&self) {
130        self.regs()
131            .out_link()
132            .modify(|_, w| w.outlink_start().set_bit());
133    }
134
135    fn stop(&self) {
136        self.regs()
137            .out_link()
138            .modify(|_, w| w.outlink_stop().set_bit());
139    }
140
141    fn restart(&self) {
142        self.regs()
143            .out_link()
144            .modify(|_, w| w.outlink_restart().set_bit());
145    }
146
147    fn set_check_owner(&self, check_owner: Option<bool>) {
148        if check_owner == Some(true) {
149            panic!("Crypto DMA does not support checking descriptor ownership");
150        }
151    }
152
153    #[cfg(dma_ext_mem_configurable_block_size)]
154    fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) {
155        self.regs()
156            .conf1()
157            .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
158    }
159
160    #[cfg(dma_can_access_psram)]
161    fn can_access_psram(&self) -> bool {
162        true
163    }
164
165    fn compatible_peripherals(&self) -> &[u8] {
166        self.0.info().compatible_peripherals
167    }
168}
169
170impl TxRegisterAccess for CryptoDmaTxChannel<'_> {
171    fn is_fifo_empty(&self) -> bool {
172        self.regs().state1().read().outfifo_cnt_debug().bits() == 0
173    }
174
175    fn set_auto_write_back(&self, enable: bool) {
176        self.regs()
177            .conf()
178            .modify(|_, w| w.out_auto_wrback().bit(enable));
179    }
180
181    fn last_dscr_address(&self) -> usize {
182        self.regs()
183            .out_eof_des_addr()
184            .read()
185            .out_eof_des_addr()
186            .bits() as usize
187    }
188
189    fn peripheral_interrupt(&self) -> Option<Interrupt> {
190        None
191    }
192
193    fn async_handler(&self) -> Option<InterruptHandler> {
194        None
195    }
196}
197
198impl InterruptAccess<DmaTxInterrupt> for CryptoDmaTxChannel<'_> {
199    fn enable_listen(&self, interrupts: EnumSet<DmaTxInterrupt>, enable: bool) {
200        self.regs().int_ena().modify(|_, w| {
201            for interrupt in interrupts {
202                match interrupt {
203                    DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable),
204                    DmaTxInterrupt::DescriptorError => w.out_dscr_err().bit(enable),
205                    DmaTxInterrupt::Eof => w.out_eof().bit(enable),
206                    DmaTxInterrupt::Done => w.out_done().bit(enable),
207                };
208            }
209            w
210        });
211    }
212
213    fn is_listening(&self) -> EnumSet<DmaTxInterrupt> {
214        let mut result = EnumSet::new();
215
216        let int_ena = self.regs().int_ena().read();
217        if int_ena.out_total_eof().bit_is_set() {
218            result |= DmaTxInterrupt::TotalEof;
219        }
220        if int_ena.out_dscr_err().bit_is_set() {
221            result |= DmaTxInterrupt::DescriptorError;
222        }
223        if int_ena.out_eof().bit_is_set() {
224            result |= DmaTxInterrupt::Eof;
225        }
226        if int_ena.out_done().bit_is_set() {
227            result |= DmaTxInterrupt::Done;
228        }
229
230        result
231    }
232
233    fn clear(&self, interrupts: impl Into<EnumSet<DmaTxInterrupt>>) {
234        self.regs().int_clr().write(|w| {
235            for interrupt in interrupts.into() {
236                match interrupt {
237                    DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(),
238                    DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(),
239                    DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(),
240                    DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(),
241                };
242            }
243            w
244        });
245    }
246
247    fn pending_interrupts(&self) -> EnumSet<DmaTxInterrupt> {
248        let mut result = EnumSet::new();
249
250        let int_raw = self.regs().int_raw().read();
251        if int_raw.out_total_eof().bit_is_set() {
252            result |= DmaTxInterrupt::TotalEof;
253        }
254        if int_raw.out_dscr_err().bit_is_set() {
255            result |= DmaTxInterrupt::DescriptorError;
256        }
257        if int_raw.out_eof().bit_is_set() {
258            result |= DmaTxInterrupt::Eof;
259        }
260        if int_raw.out_done().bit_is_set() {
261            result |= DmaTxInterrupt::Done;
262        }
263
264        result
265    }
266
267    fn waker(&self) -> &'static AtomicWaker {
268        &self.0.state().tx_waker
269    }
270
271    fn is_async(&self) -> bool {
272        self.0.state().tx_async_flag.load(Ordering::Acquire)
273    }
274
275    fn set_async(&self, is_async: bool) {
276        self.0
277            .state()
278            .tx_async_flag
279            .store(is_async, Ordering::Release);
280    }
281}
282
283impl RegisterAccess for CryptoDmaRxChannel<'_> {
284    #[allow(private_interfaces)]
285    fn enable(&self) -> Option<PeripheralGuard> {
286        Some(PeripheralGuard::new(Peripheral::CryptoDma))
287    }
288
289    fn reset(&self) {
290        self.regs().conf().toggle(|w, bit| {
291            w.in_rst().bit(bit);
292            w.ahbm_rst().bit(bit);
293            w.ahbm_fifo_rst().bit(bit)
294        });
295    }
296
297    fn set_burst_mode(&self, _burst_mode: BurstConfig) {}
298
299    fn set_descr_burst_mode(&self, burst_mode: bool) {
300        self.regs()
301            .conf()
302            .modify(|_, w| w.indscr_burst_en().bit(burst_mode));
303    }
304
305    fn set_peripheral(&self, peripheral: u8) {
306        use esp32s2::crypto_dma::aes_sha_select::SELECT;
307        let sel = match peripheral {
308            p if p == DmaPeripheral::AES.0 => SELECT::Aes,
309            p if p == DmaPeripheral::SHA.0 => SELECT::Sha,
310            _ => unreachable!(),
311        };
312        self.regs()
313            .aes_sha_select()
314            .modify(|_, w| w.select().variant(sel));
315    }
316
317    fn set_link_addr(&self, address: u32) {
318        self.regs()
319            .in_link()
320            .modify(|_, w| unsafe { w.inlink_addr().bits(address) });
321    }
322
323    fn start(&self) {
324        self.regs()
325            .in_link()
326            .modify(|_, w| w.inlink_start().set_bit());
327    }
328
329    fn stop(&self) {
330        self.regs()
331            .in_link()
332            .modify(|_, w| w.inlink_stop().set_bit());
333    }
334
335    fn restart(&self) {
336        self.regs()
337            .in_link()
338            .modify(|_, w| w.inlink_restart().set_bit());
339    }
340
341    fn set_check_owner(&self, check_owner: Option<bool>) {
342        if check_owner == Some(true) {
343            panic!("Crypto DMA does not support checking descriptor ownership");
344        }
345    }
346
347    #[cfg(dma_ext_mem_configurable_block_size)]
348    fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) {
349        self.regs()
350            .conf1()
351            .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
352    }
353
354    #[cfg(dma_can_access_psram)]
355    fn can_access_psram(&self) -> bool {
356        true
357    }
358
359    fn compatible_peripherals(&self) -> &[u8] {
360        self.0.info().compatible_peripherals
361    }
362}
363
364impl RxRegisterAccess for CryptoDmaRxChannel<'_> {
365    #[cfg(dma_supports_mem2mem)]
366    fn set_mem2mem_mode(&self, en: bool) {
367        self.regs().conf().modify(|_, w| w.mem_trans_en().bit(en));
368    }
369
370    fn peripheral_interrupt(&self) -> Option<Interrupt> {
371        // We don't know if the channel is used by AES or SHA, so interrupt handler
372        // setup is the responsibility of the peripheral driver.
373        None
374    }
375
376    fn async_handler(&self) -> Option<InterruptHandler> {
377        None
378    }
379}
380
381impl InterruptAccess<DmaRxInterrupt> for CryptoDmaRxChannel<'_> {
382    fn enable_listen(&self, interrupts: EnumSet<DmaRxInterrupt>, enable: bool) {
383        self.regs().int_ena().modify(|_, w| {
384            for interrupt in interrupts {
385                match interrupt {
386                    DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable),
387                    DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable),
388                    DmaRxInterrupt::DescriptorError => w.in_dscr_err().bit(enable),
389                    DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().bit(enable),
390                    DmaRxInterrupt::Done => w.in_done().bit(enable),
391                };
392            }
393            w
394        });
395    }
396
397    fn is_listening(&self) -> EnumSet<DmaRxInterrupt> {
398        let mut result = EnumSet::new();
399
400        let int_ena = self.regs().int_ena().read();
401        if int_ena.in_dscr_err().bit_is_set() {
402            result |= DmaRxInterrupt::DescriptorError;
403        }
404        if int_ena.in_dscr_empty().bit_is_set() {
405            result |= DmaRxInterrupt::DescriptorEmpty;
406        }
407        if int_ena.in_suc_eof().bit_is_set() {
408            result |= DmaRxInterrupt::SuccessfulEof;
409        }
410        if int_ena.in_err_eof().bit_is_set() {
411            result |= DmaRxInterrupt::ErrorEof;
412        }
413        if int_ena.in_done().bit_is_set() {
414            result |= DmaRxInterrupt::Done;
415        }
416
417        result
418    }
419
420    fn clear(&self, interrupts: impl Into<EnumSet<DmaRxInterrupt>>) {
421        self.regs().int_clr().write(|w| {
422            for interrupt in interrupts.into() {
423                match interrupt {
424                    DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(),
425                    DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(),
426                    DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(),
427                    DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(),
428                    DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(),
429                };
430            }
431            w
432        });
433    }
434
435    fn pending_interrupts(&self) -> EnumSet<DmaRxInterrupt> {
436        let mut result = EnumSet::new();
437
438        let int_raw = self.regs().int_raw().read();
439        if int_raw.in_dscr_err().bit_is_set() {
440            result |= DmaRxInterrupt::DescriptorError;
441        }
442        if int_raw.in_dscr_empty().bit_is_set() {
443            result |= DmaRxInterrupt::DescriptorEmpty;
444        }
445        if int_raw.in_suc_eof().bit_is_set() {
446            result |= DmaRxInterrupt::SuccessfulEof;
447        }
448        if int_raw.in_err_eof().bit_is_set() {
449            result |= DmaRxInterrupt::ErrorEof;
450        }
451        if int_raw.in_done().bit_is_set() {
452            result |= DmaRxInterrupt::Done;
453        }
454
455        result
456    }
457
458    fn waker(&self) -> &'static AtomicWaker {
459        &self.0.state().rx_waker
460    }
461
462    fn is_async(&self) -> bool {
463        self.0.state().rx_async_flag.load(Ordering::Relaxed)
464    }
465
466    fn set_async(&self, is_async: bool) {
467        self.0
468            .state()
469            .rx_async_flag
470            .store(is_async, Ordering::Relaxed);
471    }
472}
473
474/// A crypto-compatible type-erased DMA channel.
475pub type CryptoDmaChannel<'d> = DMA_CRYPTO<'d>;
476
477impl DMA_CRYPTO<'_> {
478    pub(super) fn info(&self) -> &'static ChannelInfo {
479        #[crate::handler(priority = crate::interrupt::Priority::max())]
480        fn interrupt_handler() {
481            asynch::handle_in_interrupt::<DMA_CRYPTO<'static>>();
482            asynch::handle_out_interrupt::<DMA_CRYPTO<'static>>();
483        }
484        static INFO: ChannelInfo = ChannelInfo {
485            peripheral_interrupt: Interrupt::CRYPTO_DMA,
486            async_handler: interrupt_handler,
487            compatible_peripherals: &[DmaPeripheral::AES.0, DmaPeripheral::SHA.0],
488        };
489        &INFO
490    }
491
492    pub(super) fn state(&self) -> &'static ChannelState {
493        static STATE: ChannelState = ChannelState {
494            tx_waker: AtomicWaker::new(),
495            rx_waker: AtomicWaker::new(),
496            tx_async_flag: AtomicBool::new(false),
497            rx_async_flag: AtomicBool::new(false),
498        };
499        &STATE
500    }
501}
502
503crate::dma::impl_channel_common!(CryptoDma, DMA_CRYPTO);