Skip to main content

esp_hal/spi/master/
dma.rs

1use core::{
2    cell::{Cell, UnsafeCell},
3    cmp::min,
4    mem::{ManuallyDrop, MaybeUninit},
5    pin::Pin,
6    ptr::NonNull,
7    sync::atomic::{Ordering, fence},
8    task::{Context, Poll},
9};
10
11#[cfg(feature = "unstable")]
12use embedded_hal::spi::{ErrorType, SpiBus};
13use enumset::EnumSet;
14#[cfg(place_spi_master_driver_in_ram)]
15use procmacros::ram;
16
17use super::*;
18use crate::{
19    RegisterToggle,
20    dma::{
21        CHUNK_SIZE,
22        Channel,
23        DmaDescriptor,
24        DmaEligiblePeripheral,
25        DmaRxBuf,
26        DmaRxBuffer,
27        DmaTxBuf,
28        DmaTxBuffer,
29        NoBuffer,
30        ScopedDmaRxBuf,
31        ScopedDmaTxBuf,
32        TransferDirection,
33        aligned::{DmaAlignedMut, InternalMemory},
34        asynch::DmaRxFuture,
35        prepare_for_rx,
36        prepare_for_tx,
37    },
38    pac::spi2::RegisterBlock,
39    private::DropGuard,
40    soc::is_slice_in_dram,
41    spi::{DmaError, master::low_level::SpiClockGuard},
42};
43#[cfg(dma_can_access_psram)]
44use crate::{dma::ManualWritebackBuffer, soc::is_slice_in_psram};
45
46const MAX_DMA_SIZE: usize = 32736;
47
48impl<'d> Spi<'d, Blocking> {
49    #[doc_replace(
50        "dma_channel" => {
51            cfg(spi_master_dma_engine = "SPI_DMA") => "DMA_SPI2",
52            cfg(spi_master_dma_engine = "AHB_GDMA") => "DMA_CH0",
53            cfg(spi_master_dma_engine = "AXI_GDMA") => "DMA_AXI_CH0",
54        }
55    )]
56    /// Converts the driver into an [`SpiDma`] driver that uses the specified DMA channel.
57    ///
58    /// ```rust, no_run
59    /// # {before_snippet}
60    /// use esp_hal::spi::{
61    ///     Mode,
62    ///     master::{Config, Spi},
63    /// };
64    ///
65    /// let mut spi_dma = Spi::new(
66    ///     peripherals.SPI2,
67    ///     Config::default()
68    ///         .with_frequency(Rate::from_khz(100))
69    ///         .with_mode(Mode::_0),
70    /// )?
71    /// .with_dma(peripherals.__dma_channel__);
72    /// # {after_snippet}
73    /// ```
74    #[instability::unstable]
75    pub fn with_dma(
76        self,
77        channel: impl SpiMasterDmaChannel<'d, AnySpi<'d>>,
78    ) -> SpiDma<'d, crate::Blocking> {
79        SpiDma::new_from_spi(self, channel.into())
80    }
81}
82
83#[doc_replace(
84    "dma_channel" => {
85        cfg(spi_master_dma_engine = "SPI_DMA") => "DMA_SPI2",
86        cfg(spi_master_dma_engine = "AHB_GDMA") => "DMA_CH0",
87        cfg(spi_master_dma_engine = "AXI_GDMA") => "DMA_AXI_CH0",
88    }
89)]
90/// DMA-controlled SPI driver.
91///
92/// This driver uses DMA to transfer data, allowing the CPU to continue working while the SPI
93/// transfer is in progress.
94///
95/// The driver provides two separate approaches to transferring data:
96///
97/// - The slice-based API allows transferring data from/to slices of memory. The data may be copied
98///   into an internal buffer before the transfer begins. A pair of copy buffers can be set up by
99///   passing them to [`with_buffers`](SpiDma::with_buffers) before the first transfer begins. For
100///   more details on when copying is necessary, see the documentation of the
101///   [`with_buffers`](SpiDma::with_buffers) method.
102/// - The buffer API allows transferring externally managed buffers. In this mode, the buffers to be
103///   transferred are provided by the caller. The buffer objects ensure that data is located in
104///   appropriate memory regions. The buffers and the driver object are moved into transfer objects
105///   for the duration of the transfer. These functions take [`DmaRxBuf`] and [`DmaTxBuf`] objects
106///   as arguments as well as the number of bytes to transfer, and their names end with `_buffer`
107///
108/// These approaches provide different trade-offs between memory usage / CPU overhead and ease of
109/// use. `embedded-hal` traits are implemented by the slice-based API's functions.
110///
111/// # Examples
112///
113/// ```rust, no_run
114/// # {before_snippet}
115/// use esp_hal::{
116///     dma::{DmaRxBuf, DmaTxBuf},
117///     dma_rx_buffer,
118///     dma_tx_buffer,
119///     spi::{
120///         Mode,
121///         master::{Config, Spi},
122///     },
123/// };
124///
125/// // Optional: create and set up copy buffers.
126/// let dma_rx_buf = dma_rx_buffer!(32000)?;
127/// let dma_tx_buf = dma_tx_buffer!(32000)?;
128///
129/// let mut spi = Spi::new(
130///     peripherals.SPI2,
131///     Config::default()
132///         .with_frequency(Rate::from_khz(100))
133///         .with_mode(Mode::_0),
134/// )?
135/// .with_dma(peripherals.__dma_channel__)
136/// .with_buffers(dma_rx_buf, dma_tx_buf);
137/// #
138/// # {after_snippet}
139/// ```
140#[cfg_attr(feature = "defmt", derive(defmt::Format))]
141pub struct SpiDma<'d, Dm>
142where
143    Dm: DriverMode,
144{
145    spi: SpiWrapper<'d>,
146    pub(crate) channel: Channel<Dm, SpiMasterErased<'d>>,
147}
148
149impl<Dm> crate::private::Sealed for SpiDma<'_, Dm> where Dm: DriverMode {}
150
151impl<Dm> core::fmt::Debug for SpiDma<'_, Dm>
152where
153    Dm: DriverMode + core::fmt::Debug,
154{
155    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
156        f.debug_struct("SpiDma").field("spi", &self.spi).finish()
157    }
158}
159
160#[instability::unstable]
161impl crate::interrupt::InterruptConfigurable for SpiDma<'_, Blocking> {
162    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
163        self.set_interrupt_handler(handler);
164    }
165}
166
167#[instability::unstable]
168impl<Dm> embassy_embedded_hal::SetConfig for SpiDma<'_, Dm>
169where
170    Dm: DriverMode,
171{
172    type Config = Config;
173    type ConfigError = ConfigError;
174
175    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
176        self.apply_config(config)
177    }
178}
179
180#[instability::unstable]
181impl<Dm> ErrorType for SpiDma<'_, Dm>
182where
183    Dm: DriverMode,
184{
185    type Error = Error;
186}
187
188#[instability::unstable]
189impl<Dm> SpiBus for SpiDma<'_, Dm>
190where
191    Dm: DriverMode,
192{
193    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
194        self.read(words)
195    }
196
197    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
198        self.write(words)
199    }
200
201    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
202        self.transfer(read, write)
203    }
204
205    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
206        self.transfer_in_place(words)
207    }
208
209    fn flush(&mut self) -> Result<(), Self::Error> {
210        // DMA limitation - we must ensure the transfers complete before returning
211        // to user code, otherwise the user might access the buffers while the transfer
212        // is still in progress. Therefore, there is no such thing as "flushing".
213        Ok(())
214    }
215}
216
217#[instability::unstable]
218impl embedded_hal_async::spi::SpiBus for SpiDma<'_, Async> {
219    async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
220        self.read_async(words).await
221    }
222
223    async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
224        self.write_async(words).await
225    }
226
227    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
228        self.transfer_async(read, write).await
229    }
230
231    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
232        self.transfer_in_place_async(words).await
233    }
234
235    async fn flush(&mut self) -> Result<(), Self::Error> {
236        // DMA limitation - we must ensure the transfers complete before returning
237        // to user code, otherwise the user might access the buffers while the transfer
238        // is still in progress. Therefore, there is no such thing as "flushing".
239        Ok(())
240    }
241}
242
243impl<'d> SpiDma<'d, Blocking> {
244    /// Converts the SPI driver into async mode.
245    #[instability::unstable]
246    pub fn into_async(self) -> SpiDma<'d, Async> {
247        self.spi
248            .set_interrupt_handler(self.spi.info().async_handler);
249        SpiDma {
250            spi: self.spi,
251            channel: self.channel.into_async(),
252        }
253    }
254
255    fn new_inner(spi: SpiWrapper<'d>, channel: SpiMasterErased<'d>) -> Self {
256        let channel = Channel::new(channel);
257        channel.runtime_ensure_compatible(spi.spi.dma_peripheral());
258
259        let state = spi.spi.dma_state();
260
261        state.tx_transfer_in_progress.set(false);
262        state.rx_transfer_in_progress.set(false);
263
264        // Safety: The descriptors occupy their own shared cache line and are updated in a
265        // synchronised fashion.
266        let (tx_descriptors, rx_descriptors) = unsafe {
267            let descriptors = (&mut *state.descriptors.get()).get_mut().into_inner();
268            descriptors.fill(DmaDescriptor::EMPTY);
269            let (tx_descriptors, rx_descriptors) = descriptors.split_at_mut(1);
270            (
271                DmaAlignedMut::new_unchecked(tx_descriptors),
272                DmaAlignedMut::new_unchecked(rx_descriptors),
273            )
274        };
275
276        let tx_buffer = cfg_select! {
277            all(spi_master_version = "1", spi_address_workaround) => unsafe {
278                (&mut *state.default_tx_buffer.get()).get_mut().unsize()
279            },
280            _ => unsafe { DmaAlignedMut::new_unchecked(&mut [][..]) },
281        };
282
283        let rx_buffer = unwrap!(DmaRxBuf::new(rx_descriptors, unsafe {
284            DmaAlignedMut::new_unchecked(&mut [])
285        }));
286        let tx_buffer = unwrap!(DmaTxBuf::new(tx_descriptors, tx_buffer));
287
288        // The buffers must be set up when creating the driver.
289        unsafe { (&mut *state.tx_buffer.get()).write(tx_buffer.into_scoped()) };
290        unsafe { (&mut *state.rx_buffer.get()).write(rx_buffer.into_scoped()) };
291
292        Self { spi, channel }
293    }
294
295    pub(super) fn new_from_spi(
296        spi_driver: Spi<'d, Blocking>,
297        channel: SpiMasterErased<'d>,
298    ) -> Self {
299        let spi = spi_driver.spi;
300
301        Self::new_inner(spi, channel)
302    }
303
304    /// Listens for the given interrupts.
305    #[instability::unstable]
306    pub fn listen(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
307        self.driver().enable_listen(interrupts.into(), true);
308    }
309
310    /// Unlistens from the given interrupts.
311    #[instability::unstable]
312    pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
313        self.driver().enable_listen(interrupts.into(), false);
314    }
315
316    /// Returns the asserted interrupts.
317    #[instability::unstable]
318    pub fn interrupts(&mut self) -> EnumSet<SpiInterrupt> {
319        self.driver().interrupts()
320    }
321
322    /// Resets asserted interrupts.
323    #[instability::unstable]
324    pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
325        self.driver().clear_interrupts(interrupts.into());
326    }
327
328    #[cfg_attr(
329        not(multi_core),
330        doc = "Registers an interrupt handler for the peripheral."
331    )]
332    #[cfg_attr(
333        multi_core,
334        doc = "Registers an interrupt handler for the peripheral on the current core."
335    )]
336    #[doc = ""]
337    /// Replaces any previously registered interrupt handlers.
338    ///
339    /// The default/unhandled interrupt handler can be restored with
340    /// [crate::interrupt::DEFAULT_INTERRUPT_HANDLER]
341    ///
342    /// # Panics
343    ///
344    /// Panics if passed interrupt handler is invalid (e.g. has priority
345    /// `None`)
346    #[instability::unstable]
347    pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
348        self.spi.set_interrupt_handler(handler);
349    }
350}
351
352impl<'d> SpiDma<'d, Async> {
353    /// Converts the SPI instance into blocking mode.
354    #[instability::unstable]
355    pub fn into_blocking(self) -> SpiDma<'d, Blocking> {
356        self.spi.disable_peri_interrupt_on_all_cores();
357        SpiDma {
358            spi: self.spi,
359            channel: self.channel.into_blocking(),
360        }
361    }
362
363    async fn wait_for_idle_async(&mut self) {
364        if self.dma_driver().state.rx_transfer_in_progress.get() {
365            _ = DmaRxFuture::new(&mut self.channel.rx).await;
366            self.dma_driver().state.rx_transfer_in_progress.set(false);
367        }
368
369        struct Fut(Driver);
370        impl Fut {
371            const DONE_EVENTS: EnumSet<SpiInterrupt> =
372                enumset::enum_set!(SpiInterrupt::TransferDone);
373        }
374        impl Future for Fut {
375            type Output = ();
376
377            fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
378                if !self.0.interrupts().is_disjoint(Self::DONE_EVENTS) {
379                    #[cfg(any(spi_master_version = "1", spi_master_version = "2"))]
380                    // Need to poll for done-ness even after interrupt fires.
381                    if self.0.busy() {
382                        cx.waker().wake_by_ref();
383                        return Poll::Pending;
384                    }
385
386                    self.0.clear_interrupts(Self::DONE_EVENTS);
387                    return Poll::Ready(());
388                }
389
390                self.0.state.waker.register(cx.waker());
391                self.0.enable_listen(Self::DONE_EVENTS, true);
392                Poll::Pending
393            }
394        }
395        impl Drop for Fut {
396            fn drop(&mut self) {
397                self.0.enable_listen(Self::DONE_EVENTS, false);
398            }
399        }
400
401        if !self.is_done() {
402            Fut(self.driver()).await;
403        }
404
405        if self.dma_driver().state.tx_transfer_in_progress.get() {
406            // In case DMA TX buffer is bigger than what the SPI consumes, stop the DMA.
407            if !self.channel.tx.is_done() {
408                self.channel.tx.stop_transfer();
409            }
410            self.dma_driver().state.tx_transfer_in_progress.set(false);
411        }
412    }
413
414    /// Fills the given buffer with data from the bus.
415    #[instability::unstable]
416    pub async fn read_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
417        if words.is_empty() {
418            return Ok(());
419        }
420
421        let _clock = SpiClockGuard::new(self.spi.info());
422
423        self.driver().setup_full_duplex()?;
424
425        if self.use_blocking_transfer(words.len()) {
426            self.dma_driver().disable_dma();
427            return self.driver().read(words);
428        }
429
430        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
431        let mut maybe_copy_buffer = match DmaOperationKind::for_read(words) {
432            DmaOperationKind::Copied => {
433                MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
434            }
435            DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
436                descriptors: &mut descriptors,
437                #[cfg(dma_can_access_psram)]
438                align_buffer: [const { None }; 2],
439            },
440        };
441
442        if maybe_copy_buffer.chunk_size() == 0 {
443            return Err(Error::from(DmaError::BufferTooSmall));
444        }
445
446        for chunk in words.chunks_mut(maybe_copy_buffer.chunk_size()) {
447            let read_bytes = chunk.len();
448            let rx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(&mut *chunk)) };
449            let tx_buffer = unsafe { NoBuffer(self.spi.dma_state().tx_buffer().prepare()) };
450
451            self.transfer_buffers_dma_async(read_bytes, 0, rx_buffer, tx_buffer)
452                .await?;
453
454            maybe_copy_buffer.finish(chunk);
455        }
456
457        Ok(())
458    }
459
460    /// Transmits the given buffer to the bus.
461    #[instability::unstable]
462    pub async fn write_async(&mut self, words: &[u8]) -> Result<(), Error> {
463        if words.is_empty() {
464            return Ok(());
465        }
466
467        let _clock = SpiClockGuard::new(self.spi.info());
468
469        self.driver().setup_full_duplex()?;
470
471        if self.use_blocking_transfer(words.len()) {
472            self.dma_driver().disable_dma();
473            return self.driver().write(words);
474        }
475
476        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
477        let mut maybe_copy_buffer = match DmaOperationKind::for_write(words) {
478            DmaOperationKind::Copied => {
479                MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
480            }
481            DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut descriptors),
482        };
483
484        if maybe_copy_buffer.chunk_size() == 0 {
485            return Err(Error::from(DmaError::BufferTooSmall));
486        }
487
488        for chunk in words.chunks(maybe_copy_buffer.chunk_size()) {
489            let write_bytes = chunk.len();
490            let rx_buffer = unsafe { NoBuffer(self.spi.dma_state().rx_buffer().prepare()) };
491            let tx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(chunk)) };
492
493            self.transfer_buffers_dma_async(0, write_bytes, rx_buffer, tx_buffer)
494                .await?;
495        }
496
497        Ok(())
498    }
499
500    /// Transfers by writing out a buffer and reading the response from
501    /// the bus into another buffer.
502    #[instability::unstable]
503    pub async fn transfer_async(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
504        if read.is_empty() && write.is_empty() {
505            return Ok(());
506        }
507
508        let _clock = SpiClockGuard::new(self.spi.info());
509
510        self.driver().setup_full_duplex()?;
511
512        if self.use_blocking_transfer(read.len().max(write.len())) {
513            self.dma_driver().disable_dma();
514            return if read.is_empty() {
515                self.driver().write(write)
516            } else if write.is_empty() {
517                self.driver().read(read)
518            } else {
519                self.driver().transfer(read, write)
520            };
521        }
522
523        let common_length = min(read.len(), write.len());
524        let (read_common, read_remainder) = read.split_at_mut(common_length);
525        let (write_common, write_remainder) = write.split_at(common_length);
526
527        // DmaOperationKind must be determined on the sub-slices actually passed to DMA.
528        let mut rx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
529        let mut maybe_copy_rx_buffer = match DmaOperationKind::for_read(read_common) {
530            DmaOperationKind::Copied => {
531                MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
532            }
533            DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
534                descriptors: &mut rx_descriptors,
535                #[cfg(dma_can_access_psram)]
536                align_buffer: [const { None }; 2],
537            },
538        };
539
540        let mut tx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
541        let mut maybe_copy_tx_buffer = match DmaOperationKind::for_write(write_common) {
542            DmaOperationKind::Copied => {
543                MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
544            }
545            DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut tx_descriptors),
546        };
547
548        let chunk_size = min(
549            maybe_copy_rx_buffer.chunk_size(),
550            maybe_copy_tx_buffer.chunk_size(),
551        );
552
553        if chunk_size == 0 {
554            return Err(Error::from(DmaError::BufferTooSmall));
555        }
556
557        for (read_chunk, write_chunk) in read_common
558            .chunks_mut(chunk_size)
559            .zip(write_common.chunks(chunk_size))
560        {
561            let read_bytes = read_chunk.len();
562            let write_bytes = write_chunk.len();
563            let tx_buffer = unsafe { maybe_copy_tx_buffer.setup(NonNull::from(write_chunk)) };
564            let rx_buffer = unsafe { maybe_copy_rx_buffer.setup(NonNull::from(&mut *read_chunk)) };
565
566            self.transfer_buffers_dma_async(read_bytes, write_bytes, rx_buffer, tx_buffer)
567                .await?;
568
569            maybe_copy_rx_buffer.finish(read_chunk);
570        }
571
572        if !read_remainder.is_empty() {
573            self.read_async(read_remainder).await
574        } else if !write_remainder.is_empty() {
575            self.write_async(write_remainder).await
576        } else {
577            Ok(())
578        }
579    }
580
581    /// Transfers by writing out a buffer and reading the response from
582    /// the bus into the same buffer.
583    #[instability::unstable]
584    pub async fn transfer_in_place_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
585        if words.is_empty() {
586            return Ok(());
587        }
588
589        let _clock = SpiClockGuard::new(self.spi.info());
590        self.driver().setup_full_duplex()?;
591
592        if self.use_blocking_transfer(words.len()) {
593            self.dma_driver().disable_dma();
594            return self.driver().transfer_in_place(words);
595        }
596
597        let mut rx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
598        let mut tx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
599        let (mut maybe_copy_rx_buffer, mut maybe_copy_tx_buffer) =
600            match DmaOperationKind::for_write(words) {
601                DmaOperationKind::Copied => (
602                    MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() }),
603                    MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() }),
604                ),
605                DmaOperationKind::InPlace => (
606                    MaybeCopyRxBuf::Direct {
607                        descriptors: &mut rx_descriptors,
608                        #[cfg(dma_can_access_psram)]
609                        align_buffer: [const { None }; 2],
610                    },
611                    MaybeCopyTxBuf::Direct(&mut tx_descriptors),
612                ),
613            };
614
615        let chunk_size = min(
616            maybe_copy_rx_buffer.chunk_size(),
617            maybe_copy_tx_buffer.chunk_size(),
618        );
619
620        if chunk_size == 0 {
621            return Err(Error::from(DmaError::BufferTooSmall));
622        }
623
624        for chunk in words.chunks_mut(chunk_size) {
625            let bytes = chunk.len();
626            let ptr = NonNull::from(&mut *chunk);
627            let tx_buffer = unsafe { maybe_copy_tx_buffer.setup(ptr) };
628            let rx_buffer = unsafe { maybe_copy_rx_buffer.setup(ptr) };
629
630            self.transfer_buffers_dma_async(bytes, bytes, rx_buffer, tx_buffer)
631                .await?;
632
633            maybe_copy_rx_buffer.finish(chunk);
634        }
635
636        Ok(())
637    }
638
639    /// Half-duplex read.
640    ///
641    /// This performs the command, address, dummy, and data phases as a single
642    /// SPI transaction. Because command and address phases cannot be split
643    /// across multiple DMA transfers, `buffer` must fit in one DMA transfer or
644    /// in the configured internal RX copy buffer.
645    #[instability::unstable]
646    pub async fn half_duplex_read_async(
647        &mut self,
648        data_mode: DataMode,
649        cmd: Command,
650        address: Address,
651        dummy: u8,
652        buffer: &mut [u8],
653    ) -> Result<(), Error> {
654        let _clock = SpiClockGuard::new(self.spi.info());
655
656        if buffer.is_empty() {
657            let rx_buffer = unsafe { NoBuffer(self.spi.dma_state().rx_buffer().prepare()) };
658            self.half_duplex_read_dma_async(data_mode, cmd, address, dummy, 0, rx_buffer)
659                .await?;
660            return Ok(());
661        }
662
663        // Transfers below the configured threshold can skip the DMA setup cost entirely.
664        if self.use_blocking_transfer(buffer.len()) {
665            self.dma_driver().disable_dma();
666            return self
667                .driver()
668                .half_duplex_read(data_mode, cmd, address, dummy, buffer);
669        }
670
671        let operation = DmaOperationKind::for_read(buffer);
672        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
673        let mut maybe_copy_buffer = match operation {
674            DmaOperationKind::Copied => {
675                MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
676            }
677            DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
678                descriptors: &mut descriptors,
679                #[cfg(dma_can_access_psram)]
680                align_buffer: [const { None }; 2],
681            },
682        };
683
684        let chunk_size = maybe_copy_buffer.chunk_size();
685        if chunk_size == 0 {
686            return Err(Error::from(DmaError::BufferTooSmall));
687        }
688        if buffer.len() > chunk_size {
689            return match operation {
690                DmaOperationKind::Copied => Err(Error::from(DmaError::Overflow)),
691                DmaOperationKind::InPlace => Err(Error::MaxDmaTransferSizeExceeded),
692            };
693        }
694
695        let rx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(&mut *buffer)) };
696        self.half_duplex_read_dma_async(data_mode, cmd, address, dummy, buffer.len(), rx_buffer)
697            .await?;
698        maybe_copy_buffer.finish(buffer);
699
700        Ok(())
701    }
702
703    /// Half-duplex write.
704    ///
705    /// This performs the command, address, dummy, and data phases as a single
706    /// SPI transaction. Because command and address phases cannot be split
707    /// across multiple DMA transfers, `buffer` must fit in one DMA transfer or
708    /// in the configured internal TX copy buffer.
709    #[instability::unstable]
710    pub async fn half_duplex_write_async(
711        &mut self,
712        data_mode: DataMode,
713        cmd: Command,
714        address: Address,
715        dummy: u8,
716        buffer: &[u8],
717    ) -> Result<(), Error> {
718        let _clock = SpiClockGuard::new(self.spi.info());
719
720        if buffer.is_empty() {
721            let tx_buffer = unsafe { NoBuffer(self.spi.dma_state().tx_buffer().prepare()) };
722            self.half_duplex_write_dma_async(data_mode, cmd, address, dummy, 0, tx_buffer)
723                .await?;
724            return Ok(());
725        }
726
727        // Transfers below the configured threshold can skip the DMA setup cost entirely.
728        if self.use_blocking_transfer(buffer.len()) {
729            self.dma_driver().disable_dma();
730            return self
731                .driver()
732                .half_duplex_write(data_mode, cmd, address, dummy, buffer);
733        }
734
735        let operation = DmaOperationKind::for_write(buffer);
736        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
737        let mut maybe_copy_buffer = match operation {
738            DmaOperationKind::Copied => {
739                MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
740            }
741            DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut descriptors),
742        };
743
744        let chunk_size = maybe_copy_buffer.chunk_size();
745        if chunk_size == 0 {
746            return Err(Error::from(DmaError::BufferTooSmall));
747        }
748        if buffer.len() > chunk_size {
749            return match operation {
750                DmaOperationKind::Copied => Err(Error::from(DmaError::Overflow)),
751                DmaOperationKind::InPlace => Err(Error::MaxDmaTransferSizeExceeded),
752            };
753        }
754
755        let tx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(buffer)) };
756        self.half_duplex_write_dma_async(data_mode, cmd, address, dummy, buffer.len(), tx_buffer)
757            .await
758    }
759
760    async fn transfer_buffers_dma_async(
761        &mut self,
762        read_bytes: usize,
763        write_bytes: usize,
764        mut rx_buffer: impl DmaRxBuffer,
765        mut tx_buffer: impl DmaTxBuffer,
766    ) -> Result<(), Error> {
767        let _clock = SpiClockGuard::new(self.spi.info());
768
769        let mut spi = DropGuard::new(&mut *self, |spi| spi.cancel_transfer());
770        unsafe {
771            spi.start_dma_transfer(read_bytes, write_bytes, &mut rx_buffer, &mut tx_buffer)?;
772        }
773        spi.wait_for_idle_async().await;
774        spi.defuse();
775        Ok(())
776    }
777
778    async fn half_duplex_read_dma_async(
779        &mut self,
780        data_mode: DataMode,
781        cmd: Command,
782        address: Address,
783        dummy: u8,
784        bytes_to_read: usize,
785        mut rx_buffer: impl DmaRxBuffer,
786    ) -> Result<(), Error> {
787        let _clock = SpiClockGuard::new(self.spi.info());
788
789        let mut spi = DropGuard::new(&mut *self, |spi| spi.cancel_transfer());
790        unsafe {
791            spi.start_half_duplex_read(
792                data_mode,
793                cmd,
794                address,
795                dummy,
796                bytes_to_read,
797                &mut rx_buffer,
798            )?;
799        }
800        spi.wait_for_idle_async().await;
801        spi.defuse();
802        Ok(())
803    }
804
805    async fn half_duplex_write_dma_async(
806        &mut self,
807        data_mode: DataMode,
808        cmd: Command,
809        address: Address,
810        dummy: u8,
811        bytes_to_write: usize,
812        mut tx_buffer: impl DmaTxBuffer,
813    ) -> Result<(), Error> {
814        let _clock = SpiClockGuard::new(self.spi.info());
815
816        let mut spi = DropGuard::new(&mut *self, |spi| spi.cancel_transfer());
817        unsafe {
818            spi.start_half_duplex_write(
819                data_mode,
820                cmd,
821                address,
822                dummy,
823                bytes_to_write,
824                &mut tx_buffer,
825            )?;
826        }
827        spi.wait_for_idle_async().await;
828        spi.defuse();
829        Ok(())
830    }
831}
832
833// +1 to make sure we have enough descriptors to satisfy strict alignment requirements
834const LINK_DESCRIPTOR_COUNT: usize = MAX_DMA_SIZE.div_ceil(CHUNK_SIZE) + 2 + 1;
835
836enum MaybeCopyTxBuf<'a> {
837    Copy(&'a mut ScopedDmaTxBuf<'static>),
838    Direct(&'a mut [DmaDescriptor; LINK_DESCRIPTOR_COUNT]),
839}
840
841impl<'a> MaybeCopyTxBuf<'a> {
842    unsafe fn setup(&mut self, data: NonNull<[u8]>) -> NoBuffer {
843        match self {
844            MaybeCopyTxBuf::Copy(tx_buffer) => {
845                tx_buffer.as_mut_slice()[..data.len()].copy_from_slice(unsafe { data.as_ref() });
846                NoBuffer(tx_buffer.prepare())
847            }
848            MaybeCopyTxBuf::Direct(descriptors) => {
849                let (buffer, _) = unsafe { unwrap!(prepare_for_tx(&mut **descriptors, data, 1)) };
850                buffer
851            }
852        }
853    }
854
855    fn chunk_size(&self) -> usize {
856        match self {
857            MaybeCopyTxBuf::Copy(buffer) => buffer.capacity().min(MAX_DMA_SIZE),
858            MaybeCopyTxBuf::Direct(_) => MAX_DMA_SIZE,
859        }
860    }
861}
862
863#[allow(clippy::large_enum_variant)]
864enum MaybeCopyRxBuf<'a> {
865    Copy(&'a mut ScopedDmaRxBuf<'static>),
866    Direct {
867        descriptors: &'a mut [DmaDescriptor; LINK_DESCRIPTOR_COUNT],
868        #[cfg(dma_can_access_psram)]
869        align_buffer: [Option<ManualWritebackBuffer>; 2],
870    },
871}
872
873impl<'a> MaybeCopyRxBuf<'a> {
874    unsafe fn setup(&mut self, data: NonNull<[u8]>) -> NoBuffer {
875        match self {
876            MaybeCopyRxBuf::Copy(rx_buffer) => NoBuffer(rx_buffer.prepare()),
877            MaybeCopyRxBuf::Direct {
878                descriptors,
879                #[cfg(dma_can_access_psram)]
880                align_buffer,
881            } => {
882                let (buffer, _) = unsafe {
883                    prepare_for_rx(
884                        &mut **descriptors,
885                        #[cfg(dma_can_access_psram)]
886                        align_buffer,
887                        data,
888                    )
889                };
890                buffer
891            }
892        }
893    }
894
895    fn chunk_size(&self) -> usize {
896        match self {
897            MaybeCopyRxBuf::Copy(buffer) => buffer.capacity().min(MAX_DMA_SIZE),
898            MaybeCopyRxBuf::Direct { .. } => MAX_DMA_SIZE,
899        }
900    }
901
902    fn finish(&mut self, chunk: &mut [u8]) {
903        match self {
904            MaybeCopyRxBuf::Copy(buffer) => {
905                chunk.copy_from_slice(&buffer.as_slice()[..chunk.len()]);
906            }
907            MaybeCopyRxBuf::Direct {
908                #[cfg(dma_can_access_psram)]
909                align_buffer,
910                ..
911            } => {
912                #[cfg(soc_internal_memory_cached)]
913                unsafe {
914                    crate::soc::cache_invalidate_addr(chunk.as_ptr() as u32, chunk.len() as u32);
915                }
916
917                #[cfg(dma_can_access_psram)]
918                for buffer in align_buffer.iter_mut() {
919                    if let Some(buffer) = buffer.as_mut() {
920                        buffer.write_back();
921                    }
922                    *buffer = None;
923                }
924            }
925        }
926    }
927}
928
929#[derive(Clone, Copy)]
930enum DmaOperationKind {
931    /// The entire slice must be copied into the internal buffer first.
932    Copied,
933
934    /// The slice can be transferred directly, with minimal copying done for alignment.
935    InPlace,
936}
937
938impl DmaOperationKind {
939    fn compute(buffer: &[u8], direction: TransferDirection) -> Self {
940        fn is_dma_compatible(buffer: &[u8], _direction: TransferDirection) -> bool {
941            // FIXME: lazy workaround for ESP32 TX DMA alignment requirements.
942            // `prepare_for_tx` and `prepare_for_rx` should be updated to handle ESP32.
943            #[cfg(spi_master_version = "1")]
944            if !((buffer.as_ptr() as usize).is_multiple_of(4) && buffer.len().is_multiple_of(4)) {
945                return false;
946            }
947
948            if is_slice_in_dram(buffer) {
949                return true;
950            }
951            #[cfg(dma_can_access_psram)]
952            if is_slice_in_psram(buffer) {
953                #[cfg(spi_master_version = "2")]
954                if _direction == TransferDirection::In {
955                    // For some reason, having tail bytes in internal RAM causes issues, so we
956                    // force copying if the end of the PSRAM buffer is not aligned.
957                    let tail_bytes = (buffer.as_ptr() as usize + buffer.len()).wrapping_neg() & 15;
958                    if tail_bytes > 0 {
959                        return false;
960                    }
961                }
962
963                return true;
964            }
965
966            // TODO: C5+ DMA can read from flash
967
968            false
969        }
970
971        if is_dma_compatible(buffer, direction) {
972            Self::InPlace
973        } else {
974            Self::Copied
975        }
976    }
977
978    fn for_read(buffer: &mut [u8]) -> Self {
979        Self::compute(buffer, TransferDirection::In)
980    }
981
982    fn for_write(buffer: &[u8]) -> Self {
983        Self::compute(buffer, TransferDirection::Out)
984    }
985}
986
987impl<'d, Dm> SpiDma<'d, Dm>
988where
989    Dm: DriverMode,
990{
991    fn use_blocking_transfer(&self, transfer_size: usize) -> bool {
992        let threshold = self
993            .spi
994            .state()
995            .min_async_transfer_size
996            .load(Ordering::Relaxed);
997        threshold > 0 && transfer_size < threshold
998    }
999
1000    fn spi(&self) -> &SpiWrapper<'_> {
1001        &self.spi
1002    }
1003
1004    fn driver(&self) -> Driver {
1005        Driver {
1006            info: self.spi.info(),
1007            state: self.spi.state(),
1008        }
1009    }
1010
1011    fn dma_driver(&self) -> DmaDriver {
1012        DmaDriver {
1013            driver: self.driver(),
1014            state: self.spi().dma_state(),
1015            dma_peripheral: self.spi.spi.dma_peripheral(),
1016        }
1017    }
1018
1019    fn is_done(&self) -> bool {
1020        if self.driver().busy() {
1021            return false;
1022        }
1023        if self.dma_driver().state.rx_transfer_in_progress.get() {
1024            // If this is an asymmetric transfer and the RX side is smaller, the RX channel
1025            // will never be "done" as it won't have enough descriptors/buffer to receive
1026            // the EOF bit from the SPI. So instead the RX channel will hit
1027            // a "descriptor empty" which means the DMA is written as much
1028            // of the received data as possible into the buffer and
1029            // discarded the rest. The user doesn't care about this discarded data.
1030
1031            if !self.channel.rx.is_done() && !self.channel.rx.has_dscr_empty_error() {
1032                return false;
1033            }
1034        }
1035        true
1036    }
1037
1038    fn wait_for_idle(&mut self) {
1039        while !self.is_done() {
1040            // Wait for the SPI to become idle
1041        }
1042        self.dma_driver().state.rx_transfer_in_progress.set(false);
1043        self.dma_driver().state.tx_transfer_in_progress.set(false);
1044        fence(Ordering::Acquire);
1045    }
1046
1047    /// # Safety
1048    ///
1049    /// The caller must ensure to not access the buffer contents while the
1050    /// transfer is in progress. Moving the buffer itself is allowed.
1051    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1052    unsafe fn start_transfer_dma<RX: DmaRxBuffer, TX: DmaTxBuffer>(
1053        &mut self,
1054        full_duplex: bool,
1055        bytes_to_read: usize,
1056        bytes_to_write: usize,
1057        rx_buffer: &mut RX,
1058        tx_buffer: &mut TX,
1059    ) -> Result<(), Error> {
1060        if bytes_to_read > MAX_DMA_SIZE || bytes_to_write > MAX_DMA_SIZE {
1061            return Err(Error::MaxDmaTransferSizeExceeded);
1062        }
1063
1064        self.dma_driver()
1065            .state
1066            .rx_transfer_in_progress
1067            .set(bytes_to_read > 0);
1068        self.dma_driver()
1069            .state
1070            .tx_transfer_in_progress
1071            .set(bytes_to_write > 0);
1072        unsafe {
1073            self.dma_driver().start_transfer_dma(
1074                full_duplex,
1075                bytes_to_read,
1076                bytes_to_write,
1077                rx_buffer,
1078                tx_buffer,
1079                &mut self.channel,
1080            )
1081        }
1082    }
1083
1084    /// # Safety
1085    ///
1086    /// The caller must ensure that the buffers are not accessed while the
1087    /// transfer is in progress. Moving the buffers is allowed.
1088    #[cfg(all(spi_master_version = "1", spi_address_workaround))]
1089    unsafe fn set_up_address_workaround(
1090        &mut self,
1091        cmd: Command,
1092        address: Address,
1093        dummy: u8,
1094    ) -> Result<(), Error> {
1095        if dummy > 0 {
1096            // FIXME: https://github.com/esp-rs/esp-hal/issues/2240
1097            error!("Dummy bits are not supported when there is no data to write");
1098            return Err(Error::Unsupported);
1099        }
1100
1101        let buffer = unsafe { self.dma_driver().tx_buffer() };
1102
1103        let bytes_to_write = address.width().div_ceil(8);
1104        // The address register is read in big-endian order,
1105        // we have to prepare the emulated write in the same way.
1106        let addr_bytes = address.value().to_be_bytes();
1107        let addr_bytes = &addr_bytes[4 - bytes_to_write..][..bytes_to_write];
1108        buffer.fill(addr_bytes);
1109
1110        self.driver().setup_half_duplex(
1111            true,
1112            cmd,
1113            Address::None,
1114            false,
1115            dummy,
1116            bytes_to_write == 0,
1117            address.mode(),
1118        )?;
1119
1120        let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
1121
1122        unsafe { self.start_transfer_dma(false, 0, bytes_to_write, rx_buffer, buffer) }
1123    }
1124
1125    fn cancel_transfer(&mut self) {
1126        let state = self.dma_driver().state;
1127        if state.tx_transfer_in_progress.get() || state.rx_transfer_in_progress.get() {
1128            self.dma_driver().abort_transfer();
1129
1130            // We need to stop the DMA transfer, too.
1131            if state.tx_transfer_in_progress.get() {
1132                self.channel.tx.stop_transfer();
1133                state.tx_transfer_in_progress.set(false);
1134            }
1135            if state.rx_transfer_in_progress.get() {
1136                self.channel.rx.stop_transfer();
1137                state.rx_transfer_in_progress.set(false);
1138            }
1139        }
1140    }
1141
1142    /// # Safety
1143    ///
1144    /// The caller must ensure that the buffers are not accessed while the
1145    /// transfer is in progress. Moving the buffers is allowed.
1146    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1147    unsafe fn start_dma_write(
1148        &mut self,
1149        bytes_to_write: usize,
1150        buffer: &mut impl DmaTxBuffer,
1151    ) -> Result<(), Error> {
1152        let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
1153
1154        unsafe { self.start_dma_transfer(0, bytes_to_write, rx_buffer, buffer) }
1155    }
1156
1157    /// Assigns copy buffers to the SPI driver.
1158    ///
1159    /// These buffers will be used to copy data when using the slice-based transfer functions.
1160    ///
1161    /// Data is copied in two cases:
1162    ///   - When the buffer is not located in a memory region that can be accessed by the DMA.
1163    #[cfg_attr(
1164        not(spi_master_dma_can_access_flash),
1165        doc = "The DMA cannot read flash memory."
1166    )]
1167    ///   - When the alignment of the buffer does not meet the DMA's requirements, the unaligned
1168    ///     parts of the buffer are copied.
1169    #[cfg_attr(
1170        spi_master_version = "1",
1171        doc = "On ESP32, transferring from internal SRAM requires copying the entire buffer if it is
1172not 4-byte aligned. This is a limitation of the current implementation."
1173    )]
1174    #[cfg_attr(
1175        spi_master_version = "2",
1176        doc = "On ESP32-S2, receiving into PSRAM requires the buffer's _end_ to be 16-byte
1177aligned, otherwise the driver requires copying the entire buffer."
1178    )]
1179    #[doc = ""]
1180    /// The maximum useful size for these buffers is 32736 bytes, any additional memory will
1181    /// be wasted.
1182    ///
1183    /// For an example of how to create these buffers, see the [`SpiDma`] documentation.
1184    #[instability::unstable]
1185    pub fn with_buffers(self, dma_rx_buf: DmaRxBuf, dma_tx_buf: DmaTxBuf) -> SpiDma<'d, Dm> {
1186        unsafe {
1187            (&mut *self.spi.dma_state().rx_buffer.get()).write(dma_rx_buf.into_scoped());
1188            (&mut *self.spi.dma_state().tx_buffer.get()).write(dma_tx_buf.into_scoped());
1189        }
1190        self
1191    }
1192
1193    /// Performs a DMA write.
1194    ///
1195    /// Returns a [`SpiDmaTransfer`] that owns the buffer and the
1196    /// SPI instance. The maximum amount of data to be sent is 32736
1197    /// bytes.
1198    #[allow(clippy::type_complexity)]
1199    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1200    #[instability::unstable]
1201    pub fn write_buffer<TX: DmaTxBuffer>(
1202        mut self,
1203        bytes_to_write: usize,
1204        mut buffer: TX,
1205    ) -> Result<SpiDmaTransfer<'d, Dm, TX>, (Error, Self, TX)> {
1206        let clock = SpiClockGuard::new(self.spi.info());
1207
1208        if let Err(e) = self.driver().setup_full_duplex() {
1209            return Err((e, self, buffer));
1210        };
1211        match unsafe { self.start_dma_write(bytes_to_write, &mut buffer) } {
1212            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
1213            Err(e) => Err((e, self, buffer)),
1214        }
1215    }
1216
1217    /// # Safety
1218    ///
1219    /// The caller must ensure that the buffers are not accessed while the
1220    /// transfer is in progress. Moving the buffers is allowed.
1221    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1222    unsafe fn start_dma_read(
1223        &mut self,
1224        bytes_to_read: usize,
1225        buffer: &mut impl DmaRxBuffer,
1226    ) -> Result<(), Error> {
1227        let tx_buffer = unsafe { self.dma_driver().tx_buffer() };
1228
1229        unsafe { self.start_dma_transfer(bytes_to_read, 0, buffer, tx_buffer) }
1230    }
1231
1232    /// Performs a DMA read.
1233    ///
1234    /// Returns a [`SpiDmaTransfer`] that owns the buffer and
1235    /// the SPI instance. The maximum amount of data to be
1236    /// received is 32736 bytes.
1237    #[allow(clippy::type_complexity)]
1238    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1239    #[instability::unstable]
1240    pub fn read_buffer<RX: DmaRxBuffer>(
1241        mut self,
1242        bytes_to_read: usize,
1243        mut buffer: RX,
1244    ) -> Result<SpiDmaTransfer<'d, Dm, RX>, (Error, Self, RX)> {
1245        let clock = SpiClockGuard::new(self.spi.info());
1246
1247        if let Err(e) = self.driver().setup_full_duplex() {
1248            return Err((e, self, buffer));
1249        };
1250        match unsafe { self.start_dma_read(bytes_to_read, &mut buffer) } {
1251            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
1252            Err(e) => Err((e, self, buffer)),
1253        }
1254    }
1255
1256    /// # Safety
1257    ///
1258    /// The caller must ensure that the buffers are not accessed while the
1259    /// transfer is in progress. Moving the buffers is allowed.
1260    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1261    unsafe fn start_dma_transfer(
1262        &mut self,
1263        bytes_to_read: usize,
1264        bytes_to_write: usize,
1265        rx_buffer: &mut impl DmaRxBuffer,
1266        tx_buffer: &mut impl DmaTxBuffer,
1267    ) -> Result<(), Error> {
1268        unsafe {
1269            self.start_transfer_dma(true, bytes_to_read, bytes_to_write, rx_buffer, tx_buffer)
1270        }
1271    }
1272
1273    /// Performs a DMA transfer.
1274    ///
1275    /// Returns a [`SpiDmaTransfer`] that owns the buffers and
1276    /// the SPI instance. The maximum amount of data to be
1277    /// sent/received is 32736 bytes.
1278    #[allow(clippy::type_complexity)]
1279    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1280    #[instability::unstable]
1281    pub fn transfer_buffers<RX: DmaRxBuffer, TX: DmaTxBuffer>(
1282        mut self,
1283        bytes_to_read: usize,
1284        mut rx_buffer: RX,
1285        bytes_to_write: usize,
1286        mut tx_buffer: TX,
1287    ) -> Result<SpiDmaTransfer<'d, Dm, (RX, TX)>, (Error, Self, RX, TX)> {
1288        let clock = SpiClockGuard::new(self.spi.info());
1289
1290        if let Err(e) = self.driver().setup_full_duplex() {
1291            return Err((e, self, rx_buffer, tx_buffer));
1292        };
1293        match unsafe {
1294            self.start_dma_transfer(
1295                bytes_to_read,
1296                bytes_to_write,
1297                &mut rx_buffer,
1298                &mut tx_buffer,
1299            )
1300        } {
1301            Ok(_) => Ok(SpiDmaTransfer::new(self, (rx_buffer, tx_buffer), clock)),
1302            Err(e) => Err((e, self, rx_buffer, tx_buffer)),
1303        }
1304    }
1305
1306    /// # Safety
1307    ///
1308    /// The caller must ensure that the buffers are not accessed while the
1309    /// transfer is in progress. Moving the buffers is allowed.
1310    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1311    unsafe fn start_half_duplex_read(
1312        &mut self,
1313        data_mode: DataMode,
1314        cmd: Command,
1315        address: Address,
1316        dummy: u8,
1317        bytes_to_read: usize,
1318        buffer: &mut impl DmaRxBuffer,
1319    ) -> Result<(), Error> {
1320        self.driver().setup_half_duplex(
1321            false,
1322            cmd,
1323            address,
1324            false,
1325            dummy,
1326            bytes_to_read == 0,
1327            data_mode,
1328        )?;
1329
1330        let tx_buffer = unsafe { self.dma_driver().tx_buffer() };
1331
1332        unsafe { self.start_transfer_dma(false, bytes_to_read, 0, buffer, tx_buffer) }
1333    }
1334
1335    /// Performs a half-duplex read operation using DMA.
1336    #[allow(clippy::type_complexity)]
1337    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1338    #[instability::unstable]
1339    pub fn half_duplex_read_buffer<RX: DmaRxBuffer>(
1340        mut self,
1341        data_mode: DataMode,
1342        cmd: Command,
1343        address: Address,
1344        dummy: u8,
1345        bytes_to_read: usize,
1346        mut buffer: RX,
1347    ) -> Result<SpiDmaTransfer<'d, Dm, RX>, (Error, Self, RX)> {
1348        let clock = SpiClockGuard::new(self.spi.info());
1349
1350        match unsafe {
1351            self.start_half_duplex_read(data_mode, cmd, address, dummy, bytes_to_read, &mut buffer)
1352        } {
1353            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
1354            Err(e) => Err((e, self, buffer)),
1355        }
1356    }
1357
1358    /// # Safety
1359    ///
1360    /// The caller must ensure that the buffers are not accessed while the
1361    /// transfer is in progress. Moving the buffers is allowed.
1362    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1363    unsafe fn start_half_duplex_write(
1364        &mut self,
1365        data_mode: DataMode,
1366        cmd: Command,
1367        address: Address,
1368        dummy: u8,
1369        bytes_to_write: usize,
1370        buffer: &mut impl DmaTxBuffer,
1371    ) -> Result<(), Error> {
1372        #[cfg(all(spi_master_version = "1", spi_address_workaround))]
1373        {
1374            // On the ESP32, if we don't have data, the address is always sent
1375            // on a single line, regardless of its data mode.
1376            if bytes_to_write == 0 && address.mode() != DataMode::SingleTwoDataLines {
1377                return unsafe { self.set_up_address_workaround(cmd, address, dummy) };
1378            }
1379        }
1380
1381        self.driver().setup_half_duplex(
1382            true,
1383            cmd,
1384            address,
1385            false,
1386            dummy,
1387            bytes_to_write == 0,
1388            data_mode,
1389        )?;
1390
1391        let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
1392
1393        unsafe { self.start_transfer_dma(false, 0, bytes_to_write, rx_buffer, buffer) }
1394    }
1395
1396    /// Performs a half-duplex write operation using DMA.
1397    #[allow(clippy::type_complexity)]
1398    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1399    #[instability::unstable]
1400    pub fn half_duplex_write_buffer<TX: DmaTxBuffer>(
1401        mut self,
1402        data_mode: DataMode,
1403        cmd: Command,
1404        address: Address,
1405        dummy: u8,
1406        bytes_to_write: usize,
1407        mut buffer: TX,
1408    ) -> Result<SpiDmaTransfer<'d, Dm, TX>, (Error, Self, TX)> {
1409        let clock = SpiClockGuard::new(self.spi.info());
1410
1411        match unsafe {
1412            self.start_half_duplex_write(
1413                data_mode,
1414                cmd,
1415                address,
1416                dummy,
1417                bytes_to_write,
1418                &mut buffer,
1419            )
1420        } {
1421            Ok(_) => Ok(SpiDmaTransfer::new(self, buffer, clock)),
1422            Err(e) => Err((e, self, buffer)),
1423        }
1424    }
1425
1426    #[doc_replace(
1427        "max_frequency" => {
1428            cfg(esp32h2) => "48MHz",
1429            _ => "80MHz",
1430        }
1431    )]
1432    /// Changes the bus configuration.
1433    ///
1434    /// # Errors
1435    ///
1436    /// [`ConfigError::FrequencyOutOfRange`] when frequency passed in config exceeds
1437    /// __max_frequency__ or is below 70 kHz.
1438    #[instability::unstable]
1439    pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
1440        self.driver().apply_config(config)
1441    }
1442
1443    fn transfer_buffers_dma(
1444        &mut self,
1445        read_bytes: usize,
1446        write_bytes: usize,
1447        mut rx_buffer: impl DmaRxBuffer,
1448        mut tx_buffer: impl DmaTxBuffer,
1449    ) -> Result<(), Error> {
1450        unsafe {
1451            self.start_dma_transfer(read_bytes, write_bytes, &mut rx_buffer, &mut tx_buffer)?;
1452        }
1453        self.wait_for_idle();
1454        Ok(())
1455    }
1456
1457    /// Reads data from the SPI bus using DMA.
1458    #[instability::unstable]
1459    pub fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
1460        let _clock = SpiClockGuard::new(self.spi.info());
1461
1462        self.driver().setup_full_duplex()?;
1463
1464        if self.use_blocking_transfer(words.len()) {
1465            self.dma_driver().disable_dma();
1466            return self.driver().read(words);
1467        }
1468
1469        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1470        let mut maybe_copy_buffer = match DmaOperationKind::for_read(words) {
1471            DmaOperationKind::Copied => {
1472                MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
1473            }
1474            DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
1475                descriptors: &mut descriptors,
1476                #[cfg(dma_can_access_psram)]
1477                align_buffer: [const { None }; 2],
1478            },
1479        };
1480
1481        if maybe_copy_buffer.chunk_size() == 0 {
1482            return Err(Error::from(DmaError::BufferTooSmall));
1483        }
1484
1485        for chunk in words.chunks_mut(maybe_copy_buffer.chunk_size()) {
1486            let read_bytes = chunk.len();
1487            let rx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(&mut *chunk)) };
1488            let tx_buffer = unsafe { NoBuffer(self.spi.dma_state().tx_buffer().prepare()) };
1489
1490            self.transfer_buffers_dma(read_bytes, 0, rx_buffer, tx_buffer)?;
1491
1492            maybe_copy_buffer.finish(chunk);
1493        }
1494
1495        Ok(())
1496    }
1497
1498    /// Writes data to the SPI bus using DMA.
1499    #[instability::unstable]
1500    pub fn write(&mut self, words: &[u8]) -> Result<(), Error> {
1501        let _clock = SpiClockGuard::new(self.spi.info());
1502
1503        self.driver().setup_full_duplex()?;
1504
1505        if self.use_blocking_transfer(words.len()) {
1506            self.dma_driver().disable_dma();
1507            return self.driver().write(words);
1508        }
1509
1510        let mut descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1511        let mut maybe_copy_buffer = match DmaOperationKind::for_write(words) {
1512            DmaOperationKind::Copied => {
1513                MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
1514            }
1515            DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut descriptors),
1516        };
1517
1518        if maybe_copy_buffer.chunk_size() == 0 {
1519            return Err(Error::from(DmaError::BufferTooSmall));
1520        }
1521
1522        for chunk in words.chunks(maybe_copy_buffer.chunk_size()) {
1523            let write_bytes = chunk.len();
1524            let rx_buffer = unsafe { NoBuffer(self.spi.dma_state().rx_buffer().prepare()) };
1525            let tx_buffer = unsafe { maybe_copy_buffer.setup(NonNull::from(chunk)) };
1526
1527            self.transfer_buffers_dma(0, write_bytes, rx_buffer, tx_buffer)?;
1528        }
1529
1530        Ok(())
1531    }
1532
1533    /// Transfers data to and from the SPI bus simultaneously using DMA.
1534    #[instability::unstable]
1535    pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
1536        let _clock = SpiClockGuard::new(self.spi.info());
1537
1538        self.driver().setup_full_duplex()?;
1539
1540        if self.use_blocking_transfer(read.len().max(write.len())) {
1541            self.dma_driver().disable_dma();
1542            if read.is_empty() {
1543                return self.driver().write(write);
1544            } else if write.is_empty() {
1545                return self.driver().read(read);
1546            } else {
1547                return self.driver().transfer(read, write);
1548            }
1549        }
1550
1551        let common_length = min(read.len(), write.len());
1552        let (read_common, read_remainder) = read.split_at_mut(common_length);
1553        let (write_common, write_remainder) = write.split_at(common_length);
1554
1555        // DmaOperationKind must be determined on the sub-slices actually passed to DMA.
1556        let mut rx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1557        let mut maybe_copy_rx_buffer = match DmaOperationKind::for_read(read_common) {
1558            DmaOperationKind::Copied => {
1559                MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() })
1560            }
1561            DmaOperationKind::InPlace => MaybeCopyRxBuf::Direct {
1562                descriptors: &mut rx_descriptors,
1563                #[cfg(dma_can_access_psram)]
1564                align_buffer: [const { None }; 2],
1565            },
1566        };
1567
1568        let mut tx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1569        let mut maybe_copy_tx_buffer = match DmaOperationKind::for_write(write_common) {
1570            DmaOperationKind::Copied => {
1571                MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() })
1572            }
1573            DmaOperationKind::InPlace => MaybeCopyTxBuf::Direct(&mut tx_descriptors),
1574        };
1575
1576        let chunk_size = min(
1577            maybe_copy_rx_buffer.chunk_size(),
1578            maybe_copy_tx_buffer.chunk_size(),
1579        );
1580
1581        if chunk_size == 0 {
1582            return Err(Error::from(DmaError::BufferTooSmall));
1583        }
1584
1585        for (read_chunk, write_chunk) in read_common
1586            .chunks_mut(chunk_size)
1587            .zip(write_common.chunks(chunk_size))
1588        {
1589            let read_bytes = read_chunk.len();
1590            let write_bytes = write_chunk.len();
1591            let tx_buffer = unsafe { maybe_copy_tx_buffer.setup(NonNull::from(write_chunk)) };
1592            let rx_buffer = unsafe { maybe_copy_rx_buffer.setup(NonNull::from(&mut *read_chunk)) };
1593
1594            self.transfer_buffers_dma(read_bytes, write_bytes, rx_buffer, tx_buffer)?;
1595
1596            maybe_copy_rx_buffer.finish(read_chunk);
1597        }
1598
1599        if !read_remainder.is_empty() {
1600            self.read(read_remainder)
1601        } else if !write_remainder.is_empty() {
1602            self.write(write_remainder)
1603        } else {
1604            Ok(())
1605        }
1606    }
1607
1608    /// Transfers data in place on the SPI bus using DMA.
1609    #[instability::unstable]
1610    pub fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
1611        let _clock = SpiClockGuard::new(self.spi.info());
1612
1613        self.driver().setup_full_duplex()?;
1614
1615        if self.use_blocking_transfer(words.len()) {
1616            self.dma_driver().disable_dma();
1617            return self.driver().transfer_in_place(words);
1618        }
1619
1620        let mut rx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1621        let mut tx_descriptors = [DmaDescriptor::EMPTY; LINK_DESCRIPTOR_COUNT];
1622        let (mut maybe_copy_rx_buffer, mut maybe_copy_tx_buffer) =
1623            match DmaOperationKind::for_write(words) {
1624                DmaOperationKind::Copied => (
1625                    MaybeCopyRxBuf::Copy(unsafe { self.spi.dma_state().rx_buffer() }),
1626                    MaybeCopyTxBuf::Copy(unsafe { self.spi.dma_state().tx_buffer() }),
1627                ),
1628                DmaOperationKind::InPlace => (
1629                    MaybeCopyRxBuf::Direct {
1630                        descriptors: &mut rx_descriptors,
1631                        #[cfg(dma_can_access_psram)]
1632                        align_buffer: [const { None }; 2],
1633                    },
1634                    MaybeCopyTxBuf::Direct(&mut tx_descriptors),
1635                ),
1636            };
1637
1638        let chunk_size = min(
1639            maybe_copy_rx_buffer.chunk_size(),
1640            maybe_copy_tx_buffer.chunk_size(),
1641        );
1642
1643        if chunk_size == 0 {
1644            return Err(Error::from(DmaError::BufferTooSmall));
1645        }
1646
1647        for chunk in words.chunks_mut(chunk_size) {
1648            let bytes = chunk.len();
1649            let ptr = NonNull::from(&mut *chunk);
1650            let tx_buffer = unsafe { maybe_copy_tx_buffer.setup(ptr) };
1651            let rx_buffer = unsafe { maybe_copy_rx_buffer.setup(ptr) };
1652
1653            self.transfer_buffers_dma(bytes, bytes, rx_buffer, tx_buffer)?;
1654
1655            maybe_copy_rx_buffer.finish(chunk);
1656        }
1657
1658        Ok(())
1659    }
1660
1661    /// Half-duplex read.
1662    #[instability::unstable]
1663    pub fn half_duplex_read(
1664        &mut self,
1665        data_mode: DataMode,
1666        cmd: Command,
1667        address: Address,
1668        dummy: u8,
1669        buffer: &mut [u8],
1670    ) -> Result<(), Error> {
1671        let _clock = SpiClockGuard::new(self.spi.info());
1672
1673        let rx_buffer = unsafe { self.dma_driver().rx_buffer() };
1674        if rx_buffer.capacity() == 0 {
1675            return Err(Error::from(DmaError::BufferTooSmall));
1676        }
1677        if buffer.len() > rx_buffer.capacity() {
1678            return Err(Error::from(DmaError::Overflow));
1679        }
1680
1681        unsafe {
1682            self.start_half_duplex_read(data_mode, cmd, address, dummy, buffer.len(), rx_buffer)?;
1683        }
1684
1685        self.wait_for_idle();
1686
1687        buffer.copy_from_slice(&rx_buffer.as_slice()[..buffer.len()]);
1688
1689        Ok(())
1690    }
1691
1692    /// Half-duplex write.
1693    #[instability::unstable]
1694    pub fn half_duplex_write(
1695        &mut self,
1696        data_mode: DataMode,
1697        cmd: Command,
1698        address: Address,
1699        dummy: u8,
1700        buffer: &[u8],
1701    ) -> Result<(), Error> {
1702        let _clock = SpiClockGuard::new(self.spi.info());
1703
1704        let tx_buffer = unsafe { self.dma_driver().tx_buffer() };
1705        if tx_buffer.capacity() == 0 {
1706            return Err(Error::from(DmaError::BufferTooSmall));
1707        }
1708        if buffer.len() > tx_buffer.capacity() {
1709            return Err(Error::from(DmaError::Overflow));
1710        }
1711
1712        tx_buffer.as_mut_slice()[..buffer.len()].copy_from_slice(buffer);
1713
1714        unsafe {
1715            self.start_half_duplex_write(data_mode, cmd, address, dummy, buffer.len(), tx_buffer)?;
1716        }
1717
1718        self.wait_for_idle();
1719
1720        Ok(())
1721    }
1722}
1723
1724/// A structure representing a DMA transfer for SPI.
1725///
1726/// Holds references to the SPI instance, DMA buffers, and transfer status.
1727#[instability::unstable]
1728pub struct SpiDmaTransfer<'d, Dm, Buf>
1729where
1730    Dm: DriverMode,
1731{
1732    spi_dma: ManuallyDrop<SpiDma<'d, Dm>>,
1733    dma_buf: ManuallyDrop<Buf>,
1734    clock: ManuallyDrop<SpiClockGuard>,
1735}
1736
1737impl<Buf> SpiDmaTransfer<'_, Async, Buf> {
1738    /// Waits for the DMA transfer to complete asynchronously.
1739    ///
1740    /// Awaits the completion of both RX and TX operations.
1741    #[instability::unstable]
1742    pub async fn wait_for_done(&mut self) {
1743        self.spi_dma.wait_for_idle_async().await;
1744    }
1745}
1746
1747impl<'d, Dm, Buf> SpiDmaTransfer<'d, Dm, Buf>
1748where
1749    Dm: DriverMode,
1750{
1751    fn new(spi_dma: SpiDma<'d, Dm>, dma_buf: Buf, clock: SpiClockGuard) -> Self {
1752        Self {
1753            spi_dma: ManuallyDrop::new(spi_dma),
1754            dma_buf: ManuallyDrop::new(dma_buf),
1755            clock: ManuallyDrop::new(clock),
1756        }
1757    }
1758
1759    /// Returns whether the transfer is complete.
1760    ///
1761    /// Both RX and TX operations are done, and the SPI instance is no longer
1762    /// busy.
1763    #[instability::unstable]
1764    pub fn is_done(&self) -> bool {
1765        self.spi_dma.is_done()
1766    }
1767
1768    /// Waits for the DMA transfer to complete.
1769    ///
1770    /// Blocks until the transfer is finished and returns the
1771    /// `SpiDma` instance and the associated buffer.
1772    #[instability::unstable]
1773    pub fn wait(mut self) -> (SpiDma<'d, Dm>, Buf) {
1774        self.spi_dma.wait_for_idle();
1775        let retval = unsafe {
1776            (
1777                ManuallyDrop::take(&mut self.spi_dma),
1778                ManuallyDrop::take(&mut self.dma_buf),
1779            )
1780        };
1781        let _ = unsafe { ManuallyDrop::take(&mut self.clock) };
1782        core::mem::forget(self);
1783        retval
1784    }
1785
1786    /// Cancels the DMA transfer.
1787    #[instability::unstable]
1788    pub fn cancel(&mut self) {
1789        if !self.spi_dma.is_done() {
1790            self.spi_dma.cancel_transfer();
1791        }
1792    }
1793}
1794
1795impl<Dm, Buf> Drop for SpiDmaTransfer<'_, Dm, Buf>
1796where
1797    Dm: DriverMode,
1798{
1799    fn drop(&mut self) {
1800        if !self.is_done() {
1801            self.spi_dma.cancel_transfer();
1802            self.spi_dma.wait_for_idle();
1803        }
1804
1805        unsafe {
1806            ManuallyDrop::drop(&mut self.spi_dma);
1807            ManuallyDrop::drop(&mut self.dma_buf);
1808        }
1809        let _ = unsafe { ManuallyDrop::take(&mut self.clock) };
1810    }
1811}
1812
1813pub(super) struct DmaDriver {
1814    driver: Driver,
1815    dma_peripheral: crate::dma::DmaPeripheral,
1816    state: &'static DmaState,
1817}
1818
1819impl DmaDriver {
1820    unsafe fn rx_buffer(&self) -> &'static mut ScopedDmaRxBuf<'static> {
1821        unsafe { self.state.rx_buffer() }
1822    }
1823
1824    unsafe fn tx_buffer(&self) -> &'static mut ScopedDmaTxBuf<'static> {
1825        unsafe { self.state.tx_buffer() }
1826    }
1827
1828    fn abort_transfer(&self) {
1829        // The SPI peripheral is controlling how much data we transfer, so let's
1830        // update its counter.
1831        // 0 doesn't take effect on ESP32 and cuts the currently transmitted byte
1832        // immediately.
1833        // 1 seems to stop after transmitting the current byte which is somewhat less
1834        // impolite.
1835        self.driver.configure_datalen(1, 1);
1836        self.driver.update();
1837    }
1838
1839    fn disable_dma(&self) {
1840        #[cfg(not(any(spi_master_version = "1", spi_master_version = "2")))]
1841        self.regs().dma_conf().modify(|_, w| {
1842            w.dma_tx_ena().clear_bit();
1843            w.dma_rx_ena().clear_bit()
1844        });
1845
1846        // PDMA: nothing to do
1847    }
1848
1849    fn regs(&self) -> &RegisterBlock {
1850        self.driver.regs()
1851    }
1852
1853    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1854    unsafe fn start_transfer_dma<Dm: DriverMode>(
1855        &self,
1856        _full_duplex: bool,
1857        rx_len: usize,
1858        tx_len: usize,
1859        rx_buffer: &mut impl DmaRxBuffer,
1860        tx_buffer: &mut impl DmaTxBuffer,
1861        channel: &mut Channel<Dm, SpiMasterErased<'_>>,
1862    ) -> Result<(), Error> {
1863        #[cfg(spi_master_version = "2")]
1864        {
1865            // without this a transfer after a write will fail
1866            self.regs().dma_out_link().write(|w| unsafe { w.bits(0) });
1867            self.regs().dma_in_link().write(|w| unsafe { w.bits(0) });
1868        }
1869
1870        self.driver.configure_datalen(rx_len, tx_len);
1871
1872        // enable the MISO and MOSI if needed
1873        self.regs()
1874            .user()
1875            .modify(|_, w| w.usr_miso().bit(rx_len > 0).usr_mosi().bit(tx_len > 0));
1876
1877        self.enable_dma();
1878
1879        if rx_len > 0 {
1880            unsafe {
1881                channel
1882                    .rx
1883                    .prepare_transfer(self.dma_peripheral, rx_buffer)
1884                    .and_then(|_| channel.rx.start_transfer())?;
1885            }
1886        } else {
1887            #[cfg(spi_master_version = "1")]
1888            {
1889                // see https://github.com/espressif/esp-idf/commit/366e4397e9dae9d93fe69ea9d389b5743295886f
1890                // see https://github.com/espressif/esp-idf/commit/0c3653b1fd7151001143451d4aa95dbf15ee8506
1891                if _full_duplex {
1892                    self.regs()
1893                        .dma_in_link()
1894                        .modify(|_, w| unsafe { w.inlink_addr().bits(0) });
1895                    self.regs()
1896                        .dma_in_link()
1897                        .modify(|_, w| w.inlink_start().set_bit());
1898                }
1899            }
1900        }
1901        if tx_len > 0 {
1902            unsafe {
1903                channel
1904                    .tx
1905                    .prepare_transfer(self.dma_peripheral, tx_buffer)
1906                    .and_then(|_| channel.tx.start_transfer())?;
1907            }
1908        }
1909
1910        #[cfg(not(any(spi_master_version = "1", spi_master_version = "2")))]
1911        self.reset_dma();
1912
1913        self.driver.start_operation();
1914
1915        Ok(())
1916    }
1917
1918    fn enable_dma(&self) {
1919        cfg_select! {
1920            any(spi_master_version = "1", spi_master_version = "2") => {
1921                self.reset_dma();
1922            }
1923            _ => {
1924                self.regs().dma_conf().modify(|_, w| {
1925                    w.dma_tx_ena().set_bit();
1926                    w.dma_rx_ena().set_bit()
1927                });
1928            }
1929        }
1930    }
1931
1932    fn reset_dma(&self) {
1933        self.regs().dma_conf().toggle(|w, bit| {
1934            cfg_select! {
1935                any(spi_master_version = "1", spi_master_version = "2") => {
1936                    w.out_rst().bit(bit);
1937                    w.in_rst().bit(bit);
1938                    w.ahbm_fifo_rst().bit(bit);
1939                    w.ahbm_rst().bit(bit)
1940                }
1941                _ => {
1942                    w.rx_afifo_rst().bit(bit);
1943                    w.buf_afifo_rst().bit(bit);
1944                    w.dma_afifo_rst().bit(bit)
1945                }
1946            }
1947        });
1948
1949        self.clear_dma_interrupts();
1950    }
1951
1952    fn clear_dma_interrupts(&self) {
1953        self.regs().dma_int_clr().write(|w| {
1954            cfg_select! {
1955                any(spi_master_version = "1", spi_master_version = "2") => {
1956                    w.inlink_dscr_empty().clear_bit_by_one();
1957                    w.outlink_dscr_error().clear_bit_by_one();
1958                    w.inlink_dscr_error().clear_bit_by_one();
1959                    w.in_done().clear_bit_by_one();
1960                    w.in_err_eof().clear_bit_by_one();
1961                    w.in_suc_eof().clear_bit_by_one();
1962                    w.out_done().clear_bit_by_one();
1963                    w.out_eof().clear_bit_by_one();
1964                    w.out_total_eof().clear_bit_by_one()
1965                }
1966                _ => {
1967                    w.dma_infifo_full_err().clear_bit_by_one();
1968                    w.dma_outfifo_empty_err().clear_bit_by_one();
1969                    w.trans_done().clear_bit_by_one();
1970                    w.mst_rx_afifo_wfull_err().clear_bit_by_one();
1971                    w.mst_tx_afifo_rempty_err().clear_bit_by_one()
1972                }
1973            }
1974        });
1975    }
1976}
1977
1978struct DmaState {
1979    tx_transfer_in_progress: Cell<bool>,
1980    rx_transfer_in_progress: Cell<bool>,
1981
1982    rx_buffer: UnsafeCell<MaybeUninit<ScopedDmaRxBuf<'static>>>,
1983    tx_buffer: UnsafeCell<MaybeUninit<ScopedDmaTxBuf<'static>>>,
1984
1985    descriptors: UnsafeCell<InternalMemory<[DmaDescriptor; 2]>>,
1986
1987    #[cfg(all(spi_master_version = "1", spi_address_workaround))]
1988    default_tx_buffer: UnsafeCell<InternalMemory<[u8; 4]>>,
1989}
1990
1991impl DmaState {
1992    // Syntactic helper to get a mutable reference to the "empty" RX DMA buffer.
1993    //
1994    // # Safety
1995    //
1996    // The caller must ensure that Rust's aliasing rules are upheld.
1997    #[allow(
1998        clippy::mut_from_ref,
1999        reason = "Safety requirements ensure this is okay"
2000    )]
2001    unsafe fn rx_buffer(&self) -> &mut ScopedDmaRxBuf<'static> {
2002        unsafe { (&mut *self.rx_buffer.get()).assume_init_mut() }
2003    }
2004
2005    // Syntactic helper to get a mutable reference to the "empty" TX DMA buffer.
2006    //
2007    // # Safety
2008    //
2009    // The caller must ensure that Rust's aliasing rules are upheld.
2010    #[allow(
2011        clippy::mut_from_ref,
2012        reason = "Safety requirements ensure this is okay"
2013    )]
2014    unsafe fn tx_buffer(&self) -> &mut ScopedDmaTxBuf<'static> {
2015        unsafe { (&mut *self.tx_buffer.get()).assume_init_mut() }
2016    }
2017}
2018
2019// SAFETY: State belongs to the currently constructed driver instance. As such, it'll not be
2020// accessed concurrently in multiple threads.
2021unsafe impl Sync for DmaState {}
2022
2023for_each_spi_master!(
2024    (all $( ($peri:ident, $sys:ident, $sclk:ident $_cs:tt $_sio:tt $(, $is_qspi:tt)?)),* ) => {
2025        impl AnySpi<'_> {
2026            #[inline(always)]
2027            fn dma_state(&self) -> &'static DmaState {
2028                match &self.0 {
2029                    $(
2030                        super::any::Inner::$sys(_spi) => {
2031                            static DMA_STATE: DmaState = DmaState {
2032                                tx_transfer_in_progress: Cell::new(false),
2033                                rx_transfer_in_progress: Cell::new(false),
2034
2035                                rx_buffer: UnsafeCell::new(MaybeUninit::uninit()),
2036                                tx_buffer: UnsafeCell::new(MaybeUninit::uninit()),
2037
2038                                descriptors: UnsafeCell::new(InternalMemory::new([DmaDescriptor::EMPTY; 2])),
2039                                #[cfg(all(spi_master_version = "1", spi_address_workaround))]
2040                                default_tx_buffer: UnsafeCell::new(InternalMemory::new([0; 4])),
2041                            };
2042
2043                            &DMA_STATE
2044                        }
2045                    )*
2046                }
2047            }
2048        }
2049    };
2050);
2051
2052impl SpiWrapper<'_> {
2053    fn dma_state(&self) -> &'static DmaState {
2054        self.spi.dma_state()
2055    }
2056}
2057
2058with_spi_master_dma_engine! {
2059    ($engine:tt, $any_channel:ident) => {
2060        /// DMA channel trait for SPI peripherals.
2061        ///
2062        /// Implemented for each channel type that can serve a particular SPI instance `S`.
2063        #[instability::unstable]
2064        #[diagnostic::on_unimplemented(
2065            message = "The DMA channel cannot be used with this SPI peripheral",
2066            label = "This DMA channel",
2067            note = "Use a channel that matches the SPI instance."
2068        )]
2069        pub trait SpiMasterDmaChannel<'d, S>: crate::private::Sealed + Into<crate::dma::$any_channel<'d>> {}
2070
2071        crate::macros::impl_dma_channel_trait! {
2072            $engine,
2073            any_peri = AnySpi<'d>,
2074            peris = for_each_spi_master,
2075            ($peri:path, $ch:path) => {
2076                impl<'d> SpiMasterDmaChannel<'d, $peri> for $ch {}
2077            }
2078        }
2079
2080        // Proxy type so that the type-erased DMA channel can be named in the driver, regardless of the DMA engine.
2081        type SpiMasterErased<'d> = crate::dma::$any_channel<'d>;
2082    };
2083}