Skip to main content

esp_idf_hal/
spi.rs

1//! SPI peripheral control
2//!
3//! SPI0 is reserved for accessing flash and sram and therefore not usable for other purposes.
4//! SPI1 shares its external pins with SPI0 and therefore has severe restrictions in use.
5//!
6//! SPI2 & 3 can be used freely.
7//!
8//! The CS pin can be controlled by hardware on esp32 variants (contrary to the description of embedded_hal).
9//!
10//! Look at the following table to determine which driver best suits your requirements:
11//!
12//! |   |                  | SpiDeviceDriver::new | SpiDeviceDriver::new (no CS) | SpiSoftCsDeviceDriver::new | SpiBusDriver::new |
13//! |---|------------------|----------------------|------------------------------|----------------------------|-------------------|
14//! |   | Managed CS       |       Hardware       |              N               |     Software triggered     |         N         |
15//! |   | 1 device         |           Y          |              Y               |              Y             |         Y         |
16//! |   | 1-3 devices      |           Y          |              N               |              Y             |         N         |
17//! |   | 4-6 devices      |    Only on esp32CX   |              N               |              Y             |         N         |
18//! |   | More than 6      |           N          |              N               |              Y             |         N         |
19//! |   | DMA              |           Y          |              Y               |              Y             |         Y         |
20//! |   | Polling transmit |           Y          |              Y               |              Y             |         Y         |
21//! |   | ISR transmit     |           Y          |              Y               |              Y             |         Y         |
22//! |   | Async support*   |           Y          |              Y               |              Y             |         Y         |
23//!
24//! * True non-blocking async possible only when all devices attached to the SPI bus are used in async mode (i.e. calling methods `xxx_async()`
25//!   instead of their blocking `xxx()` counterparts)
26//!
27//! The [Transfer::transfer], [Write::write] and [WriteIter::write_iter] functions lock the
28//! APB frequency and therefore the requests are always run at the requested baudrate.
29//! The primitive [FullDuplex::read] and [FullDuplex::send] do not lock the APB frequency and
30//! therefore may run at a different frequency.
31//!
32//! # TODO
33//! - Slave SPI
34
35use core::borrow::{Borrow, BorrowMut};
36use core::cell::Cell;
37use core::cell::UnsafeCell;
38use core::cmp::{max, min, Ordering};
39use core::future::Future;
40use core::iter::once;
41use core::marker::PhantomData;
42use core::ptr;
43
44use embassy_sync::mutex::Mutex;
45use embedded_hal::spi::{SpiBus, SpiDevice};
46
47use esp_idf_sys::*;
48use heapless::Deque;
49
50use crate::delay::{self, Ets, BLOCK};
51use crate::gpio::{AnyOutputPin, InputPin, Level, Output, OutputMode, OutputPin, PinDriver};
52use crate::interrupt::asynch::HalIsrNotification;
53use crate::interrupt::InterruptType;
54use crate::task::embassy_sync::EspRawMutex;
55use crate::task::CriticalSection;
56
57crate::embedded_hal_error!(
58    SpiError,
59    embedded_hal::spi::Error,
60    embedded_hal::spi::ErrorKind
61);
62
63use config::{Duplex, LineWidth};
64
65pub trait Spi: Send {
66    fn device() -> spi_host_device_t;
67}
68
69/// A marker interface implemented by all SPI peripherals except SPI1 which
70/// should use a fixed set of pins
71pub trait SpiAnyPins: Spi {}
72
73#[derive(Debug, Copy, Clone, Eq, PartialEq)]
74pub enum Dma {
75    Disabled,
76    Channel1(usize),
77    Channel2(usize),
78    Auto(usize),
79}
80
81impl From<Dma> for spi_dma_chan_t {
82    fn from(dma: Dma) -> Self {
83        match dma {
84            Dma::Channel1(_) => 1,
85            Dma::Channel2(_) => 2,
86            Dma::Auto(_) => 3,
87            _ => 0,
88        }
89    }
90}
91
92impl Dma {
93    pub const fn max_transfer_size(&self) -> usize {
94        let max_transfer_size = match self {
95            Dma::Disabled => TRANS_LEN,
96            Dma::Channel1(size) | Dma::Channel2(size) | Dma::Auto(size) => *size,
97        };
98        match max_transfer_size {
99            0 => panic!("The max transfer size must be greater than 0"),
100            x if x % 4 != 0 => panic!("The max transfer size must be a multiple of 4"),
101            _ => max_transfer_size,
102        }
103    }
104}
105
106pub type SpiDriverConfig = config::DriverConfig;
107pub type SpiConfig = config::Config;
108
109/// SPI configuration
110pub mod config {
111    use crate::{interrupt::InterruptType, units::*};
112    use enumset::EnumSet;
113    use esp_idf_sys::*;
114
115    use super::Dma;
116
117    pub use embedded_hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
118
119    pub struct V02Type<T>(pub T);
120
121    impl From<V02Type<embedded_hal_0_2::spi::Polarity>> for Polarity {
122        fn from(polarity: V02Type<embedded_hal_0_2::spi::Polarity>) -> Self {
123            match polarity.0 {
124                embedded_hal_0_2::spi::Polarity::IdleHigh => Polarity::IdleHigh,
125                embedded_hal_0_2::spi::Polarity::IdleLow => Polarity::IdleLow,
126            }
127        }
128    }
129
130    impl From<V02Type<embedded_hal_0_2::spi::Phase>> for Phase {
131        fn from(phase: V02Type<embedded_hal_0_2::spi::Phase>) -> Self {
132            match phase.0 {
133                embedded_hal_0_2::spi::Phase::CaptureOnFirstTransition => {
134                    Phase::CaptureOnFirstTransition
135                }
136                embedded_hal_0_2::spi::Phase::CaptureOnSecondTransition => {
137                    Phase::CaptureOnSecondTransition
138                }
139            }
140        }
141    }
142
143    impl From<V02Type<embedded_hal_0_2::spi::Mode>> for Mode {
144        fn from(mode: V02Type<embedded_hal_0_2::spi::Mode>) -> Self {
145            Self {
146                polarity: V02Type(mode.0.polarity).into(),
147                phase: V02Type(mode.0.phase).into(),
148            }
149        }
150    }
151
152    /// Specify the communication mode with the device
153    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
154    pub enum Duplex {
155        /// Full duplex is the default
156        Full,
157        /// Half duplex in some cases
158        Half,
159        /// Use MOSI (=spid) for both sending and receiving data (implies half duplex)
160        Half3Wire,
161    }
162
163    impl Duplex {
164        pub fn as_flags(&self) -> u32 {
165            match self {
166                Duplex::Full => 0,
167                Duplex::Half => SPI_DEVICE_HALFDUPLEX,
168                Duplex::Half3Wire => SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE,
169            }
170        }
171    }
172
173    /// Specifies the order in which the bits of data should be transfered/received
174    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
175    pub enum BitOrder {
176        /// Most significant bit first (default)
177        MsbFirst,
178        /// Least significant bit first
179        LsbFirst,
180        /// Least significant bit first, when sending
181        TxLsbFirst,
182        /// Least significant bit first, when receiving
183        RxLsbFirst,
184    }
185
186    impl BitOrder {
187        pub fn as_flags(&self) -> u32 {
188            match self {
189                Self::MsbFirst => 0,
190                Self::LsbFirst => SPI_DEVICE_BIT_LSBFIRST,
191                Self::TxLsbFirst => SPI_DEVICE_TXBIT_LSBFIRST,
192                Self::RxLsbFirst => SPI_DEVICE_RXBIT_LSBFIRST,
193            }
194        }
195    }
196
197    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
198    pub enum LineWidth {
199        /// 1-bit, 2 wire duplex or 1 wire half-duplex
200        Single,
201        /// 2-bit, 2 wire half-duplex
202        Dual,
203        /// 4-bit, 4 wire half-duplex
204        Quad,
205    }
206
207    /// SPI Driver configuration
208    #[derive(Debug, Clone)]
209    pub struct DriverConfig {
210        pub dma: Dma,
211        pub intr_flags: EnumSet<InterruptType>,
212    }
213
214    impl DriverConfig {
215        pub fn new() -> Self {
216            Default::default()
217        }
218
219        #[must_use]
220        pub fn dma(mut self, dma: Dma) -> Self {
221            self.dma = dma;
222            self
223        }
224
225        #[must_use]
226        pub fn intr_flags(mut self, intr_flags: EnumSet<InterruptType>) -> Self {
227            self.intr_flags = intr_flags;
228            self
229        }
230    }
231
232    impl Default for DriverConfig {
233        fn default() -> Self {
234            Self {
235                dma: Dma::Disabled,
236                intr_flags: EnumSet::<InterruptType>::empty(),
237            }
238        }
239    }
240
241    /// SPI Device configuration
242    #[derive(Debug, Clone)]
243    pub struct Config {
244        pub baudrate: Hertz,
245        pub data_mode: Mode,
246        /// This property can be set to configure a SPI Device for being write only
247        /// Thus the flag SPI_DEVICE_NO_DUMMY will be passed on initialization and
248        /// it will unlock the possibility of using 80Mhz as the bus freq
249        /// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html#timing-considerations
250        pub write_only: bool,
251        pub duplex: Duplex,
252        pub bit_order: BitOrder,
253        pub cs_active_high: bool,
254        /// On Half-Duplex transactions: `cs_pre_delay_us % 16`  corresponds to the number of SPI bit-cycles cs should be activated before the transmission.
255        /// On Full-Duplex transactions: `cs_pre_delay_us != 0`  will add 1 microsecond of cs activation before transmission
256        pub cs_pre_delay_us: Option<u16>, // u16 as per the C struct has a uint16_t, cf: esp-idf/components/driver/spi/include/driver/spi_master.h spi_device_interface_config_t
257        ///< Amount of SPI bit-cycles the cs should stay active after the transmission (0-16)
258        pub cs_post_delay_us: Option<u8>, // u8 as per the C struct had a uint8_t, cf: esp-idf/components/driver/spi/include/driver/spi_master.h spi_device_interface_config_t
259        pub input_delay_ns: i32,
260        pub polling: bool,
261        pub allow_pre_post_delays: bool,
262        pub queue_size: usize,
263    }
264
265    impl Config {
266        pub fn new() -> Self {
267            Default::default()
268        }
269
270        #[must_use]
271        pub fn baudrate(mut self, baudrate: Hertz) -> Self {
272            self.baudrate = baudrate;
273            self
274        }
275
276        #[must_use]
277        pub fn data_mode(mut self, data_mode: Mode) -> Self {
278            self.data_mode = data_mode;
279            self
280        }
281
282        #[must_use]
283        pub fn write_only(mut self, write_only: bool) -> Self {
284            self.write_only = write_only;
285            self
286        }
287
288        #[must_use]
289        pub fn duplex(mut self, duplex: Duplex) -> Self {
290            self.duplex = duplex;
291            self
292        }
293
294        #[must_use]
295        pub fn bit_order(mut self, bit_order: BitOrder) -> Self {
296            self.bit_order = bit_order;
297            self
298        }
299
300        #[must_use]
301        pub fn cs_active_high(mut self) -> Self {
302            self.cs_active_high = true;
303            self
304        }
305
306        /// On Half-Duplex transactions: `cs_pre_delay_us % 16`  corresponds to the number of SPI bit-cycles cs should be activated before the transmission
307        /// On Full-Duplex transactions: `cs_pre_delay_us != 0`  will add 1 microsecond of cs activation before transmission
308        #[must_use]
309        pub fn cs_pre_delay_us(mut self, delay_us: u16) -> Self {
310            self.cs_pre_delay_us = Some(delay_us);
311            self
312        }
313
314        /// Add an aditional Amount of SPI bit-cycles the cs should be activated after the transmission (0-16).
315        /// This only works on half-duplex transactions.
316        #[must_use]
317        pub fn cs_post_delay_us(mut self, delay_us: u8) -> Self {
318            self.cs_post_delay_us = Some(delay_us);
319            self
320        }
321
322        #[must_use]
323        pub fn input_delay_ns(mut self, input_delay_ns: i32) -> Self {
324            self.input_delay_ns = input_delay_ns;
325            self
326        }
327
328        #[must_use]
329        pub fn polling(mut self, polling: bool) -> Self {
330            self.polling = polling;
331            self
332        }
333
334        #[must_use]
335        pub fn allow_pre_post_delays(mut self, allow_pre_post_delays: bool) -> Self {
336            self.allow_pre_post_delays = allow_pre_post_delays;
337            self
338        }
339
340        #[must_use]
341        pub fn queue_size(mut self, queue_size: usize) -> Self {
342            self.queue_size = queue_size;
343            self
344        }
345    }
346
347    impl Default for Config {
348        fn default() -> Self {
349            Self {
350                baudrate: Hertz(1_000_000),
351                data_mode: embedded_hal::spi::MODE_0,
352                write_only: false,
353                cs_active_high: false,
354                duplex: Duplex::Full,
355                bit_order: BitOrder::MsbFirst,
356                cs_pre_delay_us: None,
357                cs_post_delay_us: None,
358                input_delay_ns: 0,
359                polling: true,
360                allow_pre_post_delays: false,
361                queue_size: 1,
362            }
363        }
364    }
365
366    impl From<&Config> for spi_device_interface_config_t {
367        fn from(config: &Config) -> Self {
368            Self {
369                spics_io_num: -1,
370                clock_speed_hz: config.baudrate.0 as i32,
371                mode: data_mode_to_u8(config.data_mode),
372                queue_size: config.queue_size as i32,
373                flags: if config.write_only {
374                    SPI_DEVICE_NO_DUMMY
375                } else {
376                    0_u32
377                } | if config.cs_active_high {
378                    SPI_DEVICE_POSITIVE_CS
379                } else {
380                    0_u32
381                } | config.duplex.as_flags()
382                    | config.bit_order.as_flags(),
383                cs_ena_pretrans: config.cs_pre_delay_us.unwrap_or(0),
384                cs_ena_posttrans: config.cs_post_delay_us.unwrap_or(0),
385                ..Default::default()
386            }
387        }
388    }
389
390    fn data_mode_to_u8(data_mode: Mode) -> u8 {
391        (((data_mode.polarity == Polarity::IdleHigh) as u8) << 1)
392            | ((data_mode.phase == Phase::CaptureOnSecondTransition) as u8)
393    }
394}
395
396/// SPI transaction operation.
397///
398/// This allows composition of SPI operations into a single bus transaction.
399#[non_exhaustive]
400#[derive(Debug, PartialEq, Eq)]
401pub enum Operation<'a> {
402    /// Read data into the provided buffer.
403    Read(&'a mut [u8]),
404    /// Read data into the provided buffer with the provided line width in half-duplex mode.
405    ReadWithWidth(&'a mut [u8], LineWidth),
406    /// Write data from the provided buffer, discarding read data.
407    Write(&'a [u8]),
408    /// Write data from the provided buffer, using the provided line width in half-duplex mode,
409    /// discarding read data.
410    WriteWithWidth(&'a [u8], LineWidth),
411    /// Read data into the first buffer, while writing data from the second buffer.
412    Transfer(&'a mut [u8], &'a [u8]),
413    /// Write data out while reading data into the provided buffer.
414    TransferInPlace(&'a mut [u8]),
415    /// Delay for at least the specified number of nanoseconds.
416    DelayNs(u32),
417}
418
419pub struct SpiDriver<'d> {
420    host: u8,
421    max_transfer_size: usize,
422    #[allow(dead_code)]
423    bus_async_lock: Mutex<EspRawMutex, ()>,
424    _p: PhantomData<&'d mut ()>,
425}
426
427impl<'d> SpiDriver<'d> {
428    /// Create new instance of SPI controller for SPI1
429    ///
430    /// SPI1 can only use fixed pin for SCLK, SDO and SDI as they are shared with SPI0.
431    #[cfg(esp32)]
432    pub fn new_spi1(
433        _spi: SPI1<'d>,
434        sclk: crate::gpio::Gpio6<'d>,
435        sdo: crate::gpio::Gpio7<'d>,
436        sdi: Option<crate::gpio::Gpio8<'d>>,
437        config: &config::DriverConfig,
438    ) -> Result<Self, EspError> {
439        use crate::gpio::Pin;
440
441        let max_transfer_size = Self::new_internal(
442            SPI1::device(),
443            Some(sclk.pin() as _),
444            Some(sdo.pin() as _),
445            sdi.map(|p| p.pin() as _),
446            None,
447            None,
448            config,
449        )?;
450
451        Ok(Self {
452            host: SPI1::device() as _,
453            max_transfer_size,
454            bus_async_lock: Mutex::new(()),
455            _p: PhantomData,
456        })
457    }
458
459    /// Create new instance of SPI controller for all others
460    pub fn new<SPI: SpiAnyPins + 'd>(
461        _spi: SPI,
462        sclk: impl OutputPin + 'd,
463        sdo: impl OutputPin + 'd,
464        sdi: Option<impl InputPin + 'd>,
465        config: &config::DriverConfig,
466    ) -> Result<Self, EspError> {
467        let max_transfer_size = Self::new_internal(
468            SPI::device(),
469            Some(sclk.pin() as _),
470            Some(sdo.pin() as _),
471            sdi.map(|p| p.pin() as _),
472            None,
473            None,
474            config,
475        )?;
476
477        Ok(Self {
478            host: SPI::device() as _,
479            max_transfer_size,
480            bus_async_lock: Mutex::new(()),
481            _p: PhantomData,
482        })
483    }
484
485    pub fn new_without_sclk<SPI: SpiAnyPins + 'd>(
486        _spi: SPI,
487        sdo: impl OutputPin + 'd,
488        sdi: Option<impl InputPin + 'd>,
489        config: &config::DriverConfig,
490    ) -> Result<Self, EspError> {
491        let max_transfer_size = Self::new_internal(
492            SPI::device(),
493            None,
494            Some(sdo.pin() as _),
495            sdi.map(|p| p.pin() as _),
496            None,
497            None,
498            config,
499        )?;
500
501        Ok(Self {
502            host: SPI::device() as _,
503            max_transfer_size,
504            bus_async_lock: Mutex::new(()),
505            _p: PhantomData,
506        })
507    }
508
509    pub fn new_dual<SPI: SpiAnyPins + 'd>(
510        _spi: SPI,
511        sclk: impl OutputPin + 'd,
512        data0: impl InputPin + OutputPin + 'd,
513        data1: impl InputPin + OutputPin + 'd,
514        config: &config::DriverConfig,
515    ) -> Result<Self, EspError> {
516        let max_transfer_size = Self::new_internal(
517            SPI::device(),
518            Some(sclk.pin() as _),
519            Some(data0.pin() as _),
520            Some(data1.pin() as _),
521            None,
522            None,
523            config,
524        )?;
525
526        Ok(Self {
527            host: SPI::device() as _,
528            max_transfer_size,
529            bus_async_lock: Mutex::new(()),
530            _p: PhantomData,
531        })
532    }
533
534    pub fn new_quad<SPI: SpiAnyPins + 'd>(
535        _spi: SPI,
536        sclk: impl OutputPin + 'd,
537        data0: impl InputPin + OutputPin + 'd,
538        data1: impl InputPin + OutputPin + 'd,
539        data2: impl InputPin + OutputPin + 'd,
540        data3: impl InputPin + OutputPin + 'd,
541        config: &config::DriverConfig,
542    ) -> Result<Self, EspError> {
543        let max_transfer_size = Self::new_internal(
544            SPI::device(),
545            Some(sclk.pin() as _),
546            Some(data0.pin() as _),
547            Some(data1.pin() as _),
548            Some(data2.pin() as _),
549            Some(data3.pin() as _),
550            config,
551        )?;
552
553        Ok(Self {
554            host: SPI::device() as _,
555            max_transfer_size,
556            bus_async_lock: Mutex::new(()),
557            _p: PhantomData,
558        })
559    }
560
561    pub fn host(&self) -> spi_host_device_t {
562        self.host as _
563    }
564
565    fn new_internal(
566        host: spi_host_device_t,
567        sclk: Option<i32>,
568        sdo: Option<i32>,
569        sdi: Option<i32>,
570        data2: Option<i32>,
571        data3: Option<i32>,
572        config: &config::DriverConfig,
573    ) -> Result<usize, EspError> {
574        let max_transfer_sz = config.dma.max_transfer_size();
575        let dma_chan: spi_dma_chan_t = config.dma.into();
576
577        #[cfg(not(esp_idf_version_at_least_6_0_0))]
578        #[allow(clippy::needless_update)]
579        let bus_config = spi_bus_config_t {
580            flags: SPICOMMON_BUSFLAG_MASTER,
581            sclk_io_num: sclk.unwrap_or(-1),
582
583            data4_io_num: -1,
584            data5_io_num: -1,
585            data6_io_num: -1,
586            data7_io_num: -1,
587            __bindgen_anon_1: spi_bus_config_t__bindgen_ty_1 {
588                mosi_io_num: sdo.unwrap_or(-1),
589                //data0_io_num: -1,
590            },
591            __bindgen_anon_2: spi_bus_config_t__bindgen_ty_2 {
592                miso_io_num: sdi.unwrap_or(-1),
593                //data1_io_num: -1,
594            },
595            __bindgen_anon_3: spi_bus_config_t__bindgen_ty_3 {
596                quadwp_io_num: data2.unwrap_or(-1),
597                //data2_io_num: -1,
598            },
599            __bindgen_anon_4: spi_bus_config_t__bindgen_ty_4 {
600                quadhd_io_num: data3.unwrap_or(-1),
601                //data3_io_num: -1,
602            },
603            max_transfer_sz: max_transfer_sz as i32,
604            intr_flags: InterruptType::to_native(config.intr_flags) as _,
605            ..Default::default()
606        };
607
608        #[cfg(esp_idf_version_at_least_6_0_0)]
609        #[allow(clippy::needless_update)]
610        let bus_config = spi_bus_config_t {
611            __bindgen_anon_1: spi_bus_config_t__bindgen_ty_1 {
612                __bindgen_anon_1: spi_bus_config_t__bindgen_ty_1__bindgen_ty_1 {
613                    sclk_io_num: sclk.unwrap_or(-1),
614                    data4_io_num: -1,
615                    data5_io_num: -1,
616                    data6_io_num: -1,
617                    data7_io_num: -1,
618                    __bindgen_anon_1: spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1 {
619                        mosi_io_num: sdo.unwrap_or(-1),
620                        //data0_io_num: -1,
621                    },
622                    __bindgen_anon_2: spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_2 {
623                        miso_io_num: sdi.unwrap_or(-1),
624                        //data1_io_num: -1,
625                    },
626                    __bindgen_anon_3: spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_3 {
627                        quadwp_io_num: data2.unwrap_or(-1),
628                        //data2_io_num: -1,
629                    },
630                    __bindgen_anon_4: spi_bus_config_t__bindgen_ty_1__bindgen_ty_1__bindgen_ty_4 {
631                        quadhd_io_num: data3.unwrap_or(-1),
632                        //data3_io_num: -1,
633                    },
634                },
635            },
636
637            flags: SPICOMMON_BUSFLAG_MASTER,
638            max_transfer_sz: max_transfer_sz as i32,
639            intr_flags: InterruptType::to_native(config.intr_flags) as _,
640            ..Default::default()
641        };
642
643        esp!(unsafe { spi_bus_initialize(host, &bus_config, dma_chan) })?;
644
645        Ok(max_transfer_sz)
646    }
647}
648
649impl Drop for SpiDriver<'_> {
650    fn drop(&mut self) {
651        esp!(unsafe { spi_bus_free(self.host()) }).unwrap();
652    }
653}
654
655unsafe impl Send for SpiDriver<'_> {}
656
657pub struct SpiBusDriver<'d, T>
658where
659    T: BorrowMut<SpiDriver<'d>>,
660{
661    lock: Option<BusLock>,
662    handle: spi_device_handle_t,
663    driver: T,
664    duplex: Duplex,
665    polling: bool,
666    queue_size: usize,
667    _d: PhantomData<&'d ()>,
668}
669
670impl<'d, T> SpiBusDriver<'d, T>
671where
672    T: BorrowMut<SpiDriver<'d>>,
673{
674    pub fn new(driver: T, config: &config::Config) -> Result<Self, EspError> {
675        let mut conf: spi_device_interface_config_t = config.into();
676        conf.post_cb = Some(spi_notify);
677
678        let mut handle: spi_device_handle_t = ptr::null_mut();
679        esp!(unsafe { spi_bus_add_device(driver.borrow().host(), &conf, &mut handle as *mut _) })?;
680
681        // From here on the device is registered on the bus, while `Self` - whose `Drop` is what
682        // would remove it again - does not exist yet. Acquiring the bus lock below can fail, so
683        // the registration has to be undone by hand: leaving the device attached would leak it
684        // and, worse, make the eventual `spi_bus_free` of the owning `SpiDriver` fail with
685        // `ESP_ERR_INVALID_STATE` ("not all CSses freed") long after the fact.
686        let lock = match BusLock::new(handle) {
687            Ok(lock) => lock,
688            Err(err) => {
689                // Infallible in practice: `handle` was just handed out by `spi_bus_add_device`
690                // and cannot have transactions in flight yet, so a failure here is a bug.
691                esp!(unsafe { spi_bus_remove_device(handle) }).unwrap();
692
693                return Err(err);
694            }
695        };
696
697        Ok(Self {
698            lock: Some(lock),
699            handle,
700            driver,
701            duplex: config.duplex,
702            polling: config.polling,
703            queue_size: config.queue_size,
704            _d: PhantomData,
705        })
706    }
707
708    pub fn read(&mut self, words: &mut [u8]) -> Result<(), EspError> {
709        // Full-Duplex Mode:
710        // The internal hardware 16*4 u8 FIFO buffer (shared for read/write) is not cleared
711        // between transactions (read/write/transfer)
712        // This can lead to rewriting the internal buffer to MOSI on a read call
713
714        let chunk_size = self.driver.borrow().max_transfer_size;
715
716        let transactions = spi_read_transactions(words, chunk_size, self.duplex, LineWidth::Single);
717        spi_transmit(self.handle, transactions, self.polling, self.queue_size)?;
718
719        Ok(())
720    }
721
722    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
723    pub async fn read_async(&mut self, words: &mut [u8]) -> Result<(), EspError> {
724        let chunk_size = self.driver.borrow().max_transfer_size;
725
726        let transactions = spi_read_transactions(words, chunk_size, self.duplex, LineWidth::Single);
727        core::pin::pin!(spi_transmit_async(
728            self.handle,
729            transactions,
730            self.queue_size
731        ))
732        .await?;
733
734        Ok(())
735    }
736
737    pub fn write(&mut self, words: &[u8]) -> Result<(), EspError> {
738        // Full-Duplex Mode:
739        // The internal hardware 16*4 u8 FIFO buffer (shared for read/write) is not cleared
740        // between transactions ( read/write/transfer)
741        // This can lead to re-reading the last internal buffer MOSI msg, in case the Slave fails to send a msg
742
743        let chunk_size = self.driver.borrow().max_transfer_size;
744
745        let transactions = spi_write_transactions(words, chunk_size, LineWidth::Single);
746        spi_transmit(self.handle, transactions, self.polling, self.queue_size)?;
747
748        Ok(())
749    }
750
751    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
752    pub async fn write_async(&mut self, words: &[u8]) -> Result<(), EspError> {
753        let chunk_size = self.driver.borrow().max_transfer_size;
754
755        let transactions = spi_write_transactions(words, chunk_size, LineWidth::Single);
756        core::pin::pin!(spi_transmit_async(
757            self.handle,
758            transactions,
759            self.queue_size
760        ))
761        .await?;
762
763        Ok(())
764    }
765
766    pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), EspError> {
767        // In non-DMA mode, it will internally split the transfers every 64 bytes (max_transf_len).
768        // - If the read and write buffers are not of the same length, it will first transfer the common buffer length
769        // and then (separately aligned) the remaining buffer.
770        // - Expect a delay time between every internally split (64-byte or remainder) package.
771
772        // Half-Duplex & Half-3-Duplex Mode:
773        // Data will be split into 64-byte write/read sections.
774        // Example: write: [u8;96] - read [u8; 160]
775        // Package 1: write 64, read 64 -> Package 2: write 32, read 32 -> Package 3: write 0, read 64.
776        // Note that the first "package" is a 128-byte clock out while the later are respectively 64 bytes.
777
778        let chunk_size = self.driver.borrow().max_transfer_size;
779
780        let transactions = spi_transfer_transactions(read, write, chunk_size, self.duplex);
781        spi_transmit(self.handle, transactions, self.polling, self.queue_size)?;
782
783        Ok(())
784    }
785
786    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
787    pub async fn transfer_async(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), EspError> {
788        let chunk_size = self.driver.borrow().max_transfer_size;
789
790        let transactions = spi_transfer_transactions(read, write, chunk_size, self.duplex);
791        core::pin::pin!(spi_transmit_async(
792            self.handle,
793            transactions,
794            self.queue_size
795        ))
796        .await?;
797
798        Ok(())
799    }
800
801    pub fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), EspError> {
802        let chunk_size = self.driver.borrow().max_transfer_size;
803
804        let transactions = spi_transfer_in_place_transactions(words, chunk_size);
805        spi_transmit(self.handle, transactions, self.polling, self.queue_size)?;
806
807        Ok(())
808    }
809
810    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
811    pub async fn transfer_in_place_async(&mut self, words: &mut [u8]) -> Result<(), EspError> {
812        let chunk_size = self.driver.borrow().max_transfer_size;
813
814        let transactions = spi_transfer_in_place_transactions(words, chunk_size);
815        core::pin::pin!(spi_transmit_async(
816            self.handle,
817            transactions,
818            self.queue_size
819        ))
820        .await?;
821
822        Ok(())
823    }
824
825    pub fn flush(&mut self) -> Result<(), EspError> {
826        Ok(())
827    }
828
829    /// Run the provided [`Operation`] on the bus.
830    ///
831    /// Only Operations that result in a transfer are supported. For example,
832    /// passing an [`Operation::DelayNs`] will return an error.
833    pub fn operation(&mut self, operation: Operation<'_>) -> Result<(), EspError> {
834        if let Operation::DelayNs(_) = operation {
835            return Err(EspError::from_infallible::<ESP_ERR_INVALID_ARG>());
836        }
837
838        let chunk_size = self.driver.borrow().max_transfer_size;
839        let transactions = spi_operations(once(operation), chunk_size, self.duplex)
840            .filter_map(|t| t.transaction());
841
842        spi_transmit(self.handle, transactions, self.polling, self.queue_size)?;
843
844        Ok(())
845    }
846
847    /// Run the provided [`Operation`] on the bus.
848    ///
849    /// Only Operations that result in a transfer are supported. For example,
850    /// passing an [`Operation::DelayNs`] will return an error.
851    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
852    pub async fn operation_async(&mut self, operation: Operation<'_>) -> Result<(), EspError> {
853        if let Operation::DelayNs(_) = operation {
854            return Err(EspError::from_infallible::<ESP_ERR_INVALID_ARG>());
855        }
856
857        let chunk_size = self.driver.borrow().max_transfer_size;
858        let transactions = spi_operations(once(operation), chunk_size, self.duplex)
859            .filter_map(|t| t.transaction());
860
861        core::pin::pin!(spi_transmit_async(
862            self.handle,
863            transactions,
864            self.queue_size
865        ))
866        .await?;
867
868        Ok(())
869    }
870}
871
872impl<'d, T> Drop for SpiBusDriver<'d, T>
873where
874    T: BorrowMut<SpiDriver<'d>>,
875{
876    fn drop(&mut self) {
877        // Need to drop the lock first, because it holds the device
878        // we are about to remove below
879        self.lock = None;
880
881        esp!(unsafe { spi_bus_remove_device(self.handle) }).unwrap();
882    }
883}
884
885impl<'d, T> embedded_hal::spi::ErrorType for SpiBusDriver<'d, T>
886where
887    T: BorrowMut<SpiDriver<'d>>,
888{
889    type Error = SpiError;
890}
891
892impl<'d, T> SpiBus for SpiBusDriver<'d, T>
893where
894    T: BorrowMut<SpiDriver<'d>>,
895{
896    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
897        SpiBusDriver::read(self, words).map_err(to_spi_err)
898    }
899
900    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
901        SpiBusDriver::write(self, words).map_err(to_spi_err)
902    }
903
904    fn flush(&mut self) -> Result<(), Self::Error> {
905        SpiBusDriver::flush(self).map_err(to_spi_err)
906    }
907
908    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
909        SpiBusDriver::transfer(self, read, write).map_err(to_spi_err)
910    }
911
912    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
913        SpiBusDriver::transfer_in_place(self, words).map_err(to_spi_err)
914    }
915}
916
917#[cfg(not(esp_idf_spi_master_isr_in_iram))]
918impl<'d, T> embedded_hal_async::spi::SpiBus for SpiBusDriver<'d, T>
919where
920    T: BorrowMut<SpiDriver<'d>>,
921{
922    async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
923        SpiBusDriver::read_async(self, buf)
924            .await
925            .map_err(to_spi_err)
926    }
927
928    async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
929        SpiBusDriver::write_async(self, buf)
930            .await
931            .map_err(to_spi_err)
932    }
933
934    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
935        SpiBusDriver::transfer_async(self, read, write)
936            .await
937            .map_err(to_spi_err)
938    }
939
940    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
941        SpiBusDriver::transfer_in_place_async(self, words)
942            .await
943            .map_err(to_spi_err)
944    }
945
946    async fn flush(&mut self) -> Result<(), Self::Error> {
947        SpiBusDriver::flush(self).map_err(to_spi_err)
948    }
949}
950
951enum SpiOperation {
952    Transaction(spi_transaction_t),
953    Delay(u32),
954}
955
956impl SpiOperation {
957    pub fn transaction(self) -> Option<spi_transaction_t> {
958        if let Self::Transaction(transaction) = self {
959            Some(transaction)
960        } else {
961            None
962        }
963    }
964}
965
966pub type SpiSingleDeviceDriver<'d> = SpiDeviceDriver<'d, SpiDriver<'d>>;
967
968pub struct SpiDeviceDriver<'d, T>
969where
970    T: Borrow<SpiDriver<'d>> + 'd,
971{
972    handle: spi_device_handle_t,
973    driver: T,
974    cs_pin_configured: bool,
975    duplex: Duplex,
976    polling: bool,
977    allow_pre_post_delays: bool,
978    queue_size: usize,
979    _d: PhantomData<&'d ()>,
980}
981
982impl<'d> SpiDeviceDriver<'d, SpiDriver<'d>> {
983    #[cfg(esp32)]
984    pub fn new_single_spi1(
985        spi: SPI1<'d>,
986        sclk: crate::gpio::Gpio6<'d>,
987        sdo: crate::gpio::Gpio7<'d>,
988        sdi: Option<crate::gpio::Gpio8<'d>>,
989        cs: Option<impl OutputPin + 'd>,
990        bus_config: &config::DriverConfig,
991        config: &config::Config,
992    ) -> Result<Self, EspError> {
993        Self::new(
994            SpiDriver::new_spi1(spi, sclk, sdo, sdi, bus_config)?,
995            cs,
996            config,
997        )
998    }
999
1000    pub fn new_single<SPI: SpiAnyPins + 'd>(
1001        spi: SPI,
1002        sclk: impl OutputPin + 'd,
1003        sdo: impl OutputPin + 'd,
1004        sdi: Option<impl InputPin + 'd>,
1005        cs: Option<impl OutputPin + 'd>,
1006        bus_config: &config::DriverConfig,
1007        config: &config::Config,
1008    ) -> Result<Self, EspError> {
1009        Self::new(SpiDriver::new(spi, sclk, sdo, sdi, bus_config)?, cs, config)
1010    }
1011}
1012
1013impl<'d, T> SpiDeviceDriver<'d, T>
1014where
1015    T: Borrow<SpiDriver<'d>> + 'd,
1016{
1017    pub fn new(
1018        driver: T,
1019        cs: Option<impl OutputPin + 'd>,
1020        config: &config::Config,
1021    ) -> Result<Self, EspError> {
1022        let cs = cs.map(|cs| cs.pin() as _).unwrap_or(-1);
1023
1024        let mut conf: spi_device_interface_config_t = config.into();
1025        conf.spics_io_num = cs;
1026        conf.post_cb = Some(spi_notify);
1027
1028        let mut handle: spi_device_handle_t = ptr::null_mut();
1029        esp!(unsafe { spi_bus_add_device(driver.borrow().host(), &conf, &mut handle as *mut _) })?;
1030
1031        Ok(Self {
1032            handle,
1033            driver,
1034            cs_pin_configured: cs >= 0,
1035            duplex: config.duplex,
1036            polling: config.polling,
1037            allow_pre_post_delays: config.allow_pre_post_delays,
1038            queue_size: config.queue_size,
1039            _d: PhantomData,
1040        })
1041    }
1042
1043    pub fn device(&self) -> spi_device_handle_t {
1044        self.handle
1045    }
1046
1047    pub fn transaction(&mut self, operations: &mut [Operation<'_>]) -> Result<(), EspError> {
1048        self.run(
1049            self.hardware_cs_ctl(operations.iter_mut().map(copy_operation))?,
1050            operations.iter_mut().map(copy_operation),
1051        )
1052    }
1053
1054    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
1055    pub async fn transaction_async(
1056        &mut self,
1057        operations: &mut [Operation<'_>],
1058    ) -> Result<(), EspError> {
1059        core::pin::pin!(self.run_async(
1060            self.hardware_cs_ctl(operations.iter_mut().map(copy_operation))?,
1061            operations.iter_mut().map(copy_operation),
1062        ))
1063        .await
1064    }
1065
1066    pub fn read(&mut self, read: &mut [u8]) -> Result<(), EspError> {
1067        self.transaction(&mut [Operation::Read(read)])
1068    }
1069
1070    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
1071    pub async fn read_async(&mut self, read: &mut [u8]) -> Result<(), EspError> {
1072        let mut operation = [Operation::Read(read)];
1073        let work = core::pin::pin!(self.transaction_async(&mut operation));
1074        work.await
1075    }
1076
1077    pub fn write(&mut self, write: &[u8]) -> Result<(), EspError> {
1078        self.transaction(&mut [Operation::Write(write)])
1079    }
1080
1081    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
1082    pub async fn write_async(&mut self, write: &[u8]) -> Result<(), EspError> {
1083        let mut operation = [Operation::Write(write)];
1084        let work = core::pin::pin!(self.transaction_async(&mut operation));
1085        work.await
1086    }
1087
1088    pub fn transfer_in_place(&mut self, buf: &mut [u8]) -> Result<(), EspError> {
1089        self.transaction(&mut [Operation::TransferInPlace(buf)])
1090    }
1091
1092    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
1093    pub async fn transfer_in_place_async(&mut self, buf: &mut [u8]) -> Result<(), EspError> {
1094        let mut operation = [Operation::TransferInPlace(buf)];
1095        let work = core::pin::pin!(self.transaction_async(&mut operation));
1096        work.await
1097    }
1098
1099    pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), EspError> {
1100        self.transaction(&mut [Operation::Transfer(read, write)])
1101    }
1102
1103    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
1104    pub async fn transfer_async(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), EspError> {
1105        let mut operation = [Operation::Transfer(read, write)];
1106        let work = core::pin::pin!(self.transaction_async(&mut operation));
1107        work.await
1108    }
1109
1110    fn run<'a, 'c, 'p, M>(
1111        &mut self,
1112        mut cs_pin: CsCtl<'c, 'p, M>,
1113        operations: impl Iterator<Item = Operation<'a>> + 'a,
1114    ) -> Result<(), EspError>
1115    where
1116        M: OutputMode,
1117    {
1118        let _lock = if cs_pin.needs_bus_lock() {
1119            Some(BusLock::new(self.device())?)
1120        } else {
1121            None
1122        };
1123
1124        cs_pin.raise_cs()?;
1125
1126        let mut spi_operations = self
1127            .spi_operations(operations)
1128            .enumerate()
1129            .map(|(index, mut operation)| {
1130                cs_pin.configure(&mut operation, index);
1131                operation
1132            })
1133            .peekable();
1134
1135        let delay_impl = crate::delay::Delay::new_default();
1136        let mut result = Ok(());
1137
1138        while spi_operations.peek().is_some() {
1139            if let Some(SpiOperation::Delay(delay)) = spi_operations.peek() {
1140                delay_impl.delay_us(*delay / 1000);
1141                spi_operations.next();
1142            } else {
1143                let transactions = core::iter::from_fn(|| {
1144                    spi_operations
1145                        .next_if(|operation| matches!(operation, SpiOperation::Transaction(_)))
1146                })
1147                .fuse()
1148                .filter_map(|operation| operation.transaction());
1149
1150                result = spi_transmit(self.handle, transactions, self.polling, self.queue_size);
1151
1152                if result.is_err() {
1153                    break;
1154                }
1155            }
1156        }
1157
1158        cs_pin.lower_cs()?;
1159
1160        result
1161    }
1162
1163    #[allow(dead_code)]
1164    async fn run_async<'a, 'c, 'p, M>(
1165        &self,
1166        mut cs_pin: CsCtl<'c, 'p, M>,
1167        operations: impl Iterator<Item = Operation<'a>> + 'a,
1168    ) -> Result<(), EspError>
1169    where
1170        M: OutputMode,
1171    {
1172        let _async_bus_lock = if cs_pin.needs_bus_lock() {
1173            Some(self.driver.borrow().bus_async_lock.lock().await)
1174        } else {
1175            None
1176        };
1177
1178        let _lock = if cs_pin.needs_bus_lock() {
1179            Some(BusLock::new(self.device())?)
1180        } else {
1181            None
1182        };
1183
1184        cs_pin.raise_cs()?;
1185
1186        let delay_impl = crate::delay::Delay::new_default(); // TODO: Need to wait asnchronously if in async mode
1187        let mut result = Ok(());
1188
1189        let mut spi_operations = self
1190            .spi_operations(operations)
1191            .enumerate()
1192            .map(|(index, mut operation)| {
1193                cs_pin.configure(&mut operation, index);
1194                operation
1195            })
1196            .peekable();
1197
1198        while spi_operations.peek().is_some() {
1199            if let Some(SpiOperation::Delay(delay)) = spi_operations.peek() {
1200                delay_impl.delay_us(*delay);
1201                spi_operations.next();
1202            } else {
1203                let transactions = core::iter::from_fn(|| {
1204                    spi_operations
1205                        .next_if(|operation| matches!(operation, SpiOperation::Transaction(_)))
1206                })
1207                .fuse()
1208                .filter_map(|operation| operation.transaction());
1209
1210                result = core::pin::pin!(spi_transmit_async(
1211                    self.handle,
1212                    transactions,
1213                    self.queue_size
1214                ))
1215                .await;
1216
1217                if result.is_err() {
1218                    break;
1219                }
1220            }
1221        }
1222
1223        cs_pin.lower_cs()?;
1224
1225        result
1226    }
1227
1228    fn hardware_cs_ctl<'a, 'c, 'p>(
1229        &self,
1230        operations: impl Iterator<Item = Operation<'a>> + 'a,
1231    ) -> Result<CsCtl<'c, 'p, Output>, EspError> {
1232        let (total_count, transactions_count, first_transaction, last_transaction) =
1233            self.spi_operations_stats(operations);
1234
1235        if !self.allow_pre_post_delays
1236            && self.cs_pin_configured
1237            && transactions_count > 0
1238            && (first_transaction != Some(0) || last_transaction != Some(total_count - 1))
1239        {
1240            Err(EspError::from_infallible::<ESP_ERR_INVALID_ARG>())?;
1241        }
1242
1243        Ok(CsCtl::Hardware {
1244            enabled: self.cs_pin_configured,
1245            transactions_count,
1246            last_transaction,
1247        })
1248    }
1249
1250    fn spi_operations_stats<'a>(
1251        &self,
1252        operations: impl Iterator<Item = Operation<'a>> + 'a,
1253    ) -> (usize, usize, Option<usize>, Option<usize>) {
1254        self.spi_operations(operations).enumerate().fold(
1255            (0, 0, None, None),
1256            |(total_count, transactions_count, first_transaction, last_transaction),
1257             (index, operation)| {
1258                if matches!(operation, SpiOperation::Transaction(_)) {
1259                    (
1260                        total_count + 1,
1261                        transactions_count + 1,
1262                        Some(first_transaction.unwrap_or(index)),
1263                        Some(index),
1264                    )
1265                } else {
1266                    (
1267                        total_count + 1,
1268                        transactions_count,
1269                        first_transaction,
1270                        last_transaction,
1271                    )
1272                }
1273            },
1274        )
1275    }
1276
1277    fn spi_operations<'a>(
1278        &self,
1279        operations: impl Iterator<Item = Operation<'a>> + 'a,
1280    ) -> impl Iterator<Item = SpiOperation> + 'a {
1281        let chunk_size = self.driver.borrow().max_transfer_size;
1282        let duplex = self.duplex;
1283        spi_operations(operations, chunk_size, duplex)
1284    }
1285}
1286
1287impl<'d, T> Drop for SpiDeviceDriver<'d, T>
1288where
1289    T: Borrow<SpiDriver<'d>> + 'd,
1290{
1291    fn drop(&mut self) {
1292        esp!(unsafe { spi_bus_remove_device(self.handle) }).unwrap();
1293    }
1294}
1295
1296unsafe impl<'d, T> Send for SpiDeviceDriver<'d, T> where T: Send + Borrow<SpiDriver<'d>> + 'd {}
1297
1298impl<'d, T> embedded_hal::spi::ErrorType for SpiDeviceDriver<'d, T>
1299where
1300    T: Borrow<SpiDriver<'d>> + 'd,
1301{
1302    type Error = SpiError;
1303}
1304
1305impl<'d, T> SpiDevice for SpiDeviceDriver<'d, T>
1306where
1307    T: Borrow<SpiDriver<'d>> + 'd,
1308{
1309    fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
1310        Self::read(self, buf).map_err(to_spi_err)
1311    }
1312
1313    fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
1314        Self::write(self, buf).map_err(to_spi_err)
1315    }
1316
1317    fn transaction(
1318        &mut self,
1319        operations: &mut [embedded_hal::spi::Operation<'_, u8>],
1320    ) -> Result<(), Self::Error> {
1321        self.run(
1322            self.hardware_cs_ctl(operations.iter_mut().map(copy_ehal_operation))?,
1323            operations.iter_mut().map(copy_ehal_operation),
1324        )
1325        .map_err(to_spi_err)
1326    }
1327}
1328
1329impl<'d, T> embedded_hal_0_2::blocking::spi::Transfer<u8> for SpiDeviceDriver<'d, T>
1330where
1331    T: Borrow<SpiDriver<'d>> + 'd,
1332{
1333    type Error = SpiError;
1334
1335    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
1336        self.transfer_in_place(words)?;
1337
1338        Ok(words)
1339    }
1340}
1341
1342impl<'d, T> embedded_hal_0_2::blocking::spi::Write<u8> for SpiDeviceDriver<'d, T>
1343where
1344    T: Borrow<SpiDriver<'d>> + 'd,
1345{
1346    type Error = SpiError;
1347
1348    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
1349        self.write(words).map_err(to_spi_err)
1350    }
1351}
1352
1353/// All data is chunked into max(iter.len(), 64)
1354impl<'d, T> embedded_hal_0_2::blocking::spi::WriteIter<u8> for SpiDeviceDriver<'d, T>
1355where
1356    T: Borrow<SpiDriver<'d>> + 'd,
1357{
1358    type Error = SpiError;
1359
1360    fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
1361    where
1362        WI: IntoIterator<Item = u8>,
1363    {
1364        let mut lock = None;
1365
1366        let mut words = words.into_iter().peekable();
1367        let mut buf = [0_u8; TRANS_LEN];
1368
1369        loop {
1370            let mut offset = 0_usize;
1371
1372            while offset < buf.len() {
1373                if let Some(word) = words.next() {
1374                    buf[offset] = word;
1375                    offset += 1;
1376                } else {
1377                    break;
1378                }
1379            }
1380
1381            if offset == 0 {
1382                break;
1383            }
1384
1385            let mut transaction = spi_create_transaction(
1386                core::ptr::null_mut(),
1387                buf[..offset].as_ptr(),
1388                offset,
1389                0,
1390                LineWidth::Single,
1391            );
1392
1393            if lock.is_none() && words.peek().is_some() {
1394                lock = Some(BusLock::new(self.handle)?);
1395            }
1396
1397            set_keep_cs_active(
1398                &mut transaction,
1399                self.cs_pin_configured && words.peek().is_some(),
1400            );
1401
1402            spi_transmit(
1403                self.handle,
1404                once(transaction),
1405                self.polling,
1406                self.queue_size,
1407            )?;
1408        }
1409
1410        Ok(())
1411    }
1412}
1413
1414impl<'d, T> embedded_hal_0_2::blocking::spi::Transactional<u8> for SpiDeviceDriver<'d, T>
1415where
1416    T: Borrow<SpiDriver<'d>> + 'd,
1417{
1418    type Error = SpiError;
1419
1420    fn exec(
1421        &mut self,
1422        operations: &mut [embedded_hal_0_2::blocking::spi::Operation<'_, u8>],
1423    ) -> Result<(), Self::Error> {
1424        self.run(
1425            self.hardware_cs_ctl(operations.iter_mut().map(|op| match op {
1426                embedded_hal_0_2::blocking::spi::Operation::Write(words) => Operation::Write(words),
1427                embedded_hal_0_2::blocking::spi::Operation::Transfer(words) => {
1428                    Operation::TransferInPlace(words)
1429                }
1430            }))?,
1431            operations.iter_mut().map(|op| match op {
1432                embedded_hal_0_2::blocking::spi::Operation::Write(words) => Operation::Write(words),
1433                embedded_hal_0_2::blocking::spi::Operation::Transfer(words) => {
1434                    Operation::TransferInPlace(words)
1435                }
1436            }),
1437        )
1438        .map_err(to_spi_err)
1439    }
1440}
1441
1442#[cfg(not(esp_idf_spi_master_isr_in_iram))]
1443impl<'d, T> embedded_hal_async::spi::SpiDevice for SpiDeviceDriver<'d, T>
1444where
1445    T: Borrow<SpiDriver<'d>> + 'd,
1446{
1447    async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
1448        Self::read_async(self, buf).await.map_err(to_spi_err)
1449    }
1450
1451    async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
1452        Self::write_async(self, buf).await.map_err(to_spi_err)
1453    }
1454
1455    async fn transaction(
1456        &mut self,
1457        operations: &mut [embedded_hal::spi::Operation<'_, u8>],
1458    ) -> Result<(), Self::Error> {
1459        core::pin::pin!(self.run_async(
1460            self.hardware_cs_ctl(operations.iter_mut().map(copy_ehal_operation))?,
1461            operations.iter_mut().map(copy_ehal_operation),
1462        ))
1463        .await
1464        .map_err(to_spi_err)
1465    }
1466}
1467
1468pub struct SpiSharedDeviceDriver<'d, T>
1469where
1470    T: Borrow<SpiDriver<'d>> + 'd,
1471{
1472    driver: UnsafeCell<SpiDeviceDriver<'d, T>>,
1473    lock: CriticalSection,
1474    #[allow(dead_code)]
1475    async_lock: Mutex<EspRawMutex, ()>,
1476}
1477
1478impl<'d, T> SpiSharedDeviceDriver<'d, T>
1479where
1480    T: Borrow<SpiDriver<'d>> + 'd,
1481{
1482    pub fn new(driver: T, config: &config::Config) -> Result<Self, EspError> {
1483        Ok(Self::wrap(SpiDeviceDriver::new(
1484            driver,
1485            Option::<AnyOutputPin>::None,
1486            config,
1487        )?))
1488    }
1489
1490    pub const fn wrap(device: SpiDeviceDriver<'d, T>) -> Self {
1491        Self {
1492            driver: UnsafeCell::new(device),
1493            lock: CriticalSection::new(),
1494            async_lock: Mutex::new(()),
1495        }
1496    }
1497
1498    pub fn lock<R>(&self, f: impl FnOnce(&mut SpiDeviceDriver<'d, T>) -> R) -> R {
1499        let _guard = self.lock.enter();
1500
1501        let device = unsafe { self.driver_mut() };
1502
1503        f(device)
1504    }
1505
1506    pub fn release(self) -> SpiDeviceDriver<'d, T> {
1507        self.driver.into_inner()
1508    }
1509
1510    #[allow(clippy::mut_from_ref)]
1511    unsafe fn driver_mut(&self) -> &mut SpiDeviceDriver<'d, T> {
1512        &mut *self.driver.get()
1513    }
1514}
1515
1516pub struct SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER> {
1517    shared_device: DEVICE,
1518    cs_pin: PinDriver<'d, Output>,
1519    pre_delay_us: Option<u32>,
1520    post_delay_us: Option<u32>,
1521    _p: PhantomData<fn() -> DRIVER>,
1522}
1523
1524impl<'d, DEVICE, DRIVER> SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER>
1525where
1526    DEVICE: Borrow<SpiSharedDeviceDriver<'d, DRIVER>>,
1527    DRIVER: Borrow<SpiDriver<'d>> + 'd,
1528{
1529    pub fn new(
1530        shared_device: DEVICE,
1531        cs: impl OutputPin + 'd,
1532        cs_level: Level,
1533    ) -> Result<Self, EspError> {
1534        let mut cs_pin: PinDriver<Output> = PinDriver::output(cs)?;
1535
1536        cs_pin.set_level(cs_level)?;
1537
1538        Ok(Self {
1539            shared_device,
1540            cs_pin,
1541            pre_delay_us: None,
1542            post_delay_us: None,
1543            _p: PhantomData,
1544        })
1545    }
1546
1547    /// Add an aditional Amount of SPI bit-cycles the cs should be activated before the transmission (0-16).
1548    /// This only works on half-duplex transactions.
1549    pub fn cs_pre_delay_us(&mut self, delay_us: u32) -> &mut Self {
1550        self.pre_delay_us = Some(delay_us);
1551
1552        self
1553    }
1554
1555    /// Add an aditional delay of x in uSeconds after transaction
1556    /// between last clk out and chip select
1557    pub fn cs_post_delay_us(&mut self, delay_us: u32) -> &mut Self {
1558        self.post_delay_us = Some(delay_us);
1559
1560        self
1561    }
1562
1563    pub fn transaction(&mut self, operations: &mut [Operation<'_>]) -> Result<(), EspError> {
1564        self.run(operations.iter_mut().map(copy_operation))
1565    }
1566
1567    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
1568    pub async fn transaction_async(
1569        &mut self,
1570        operations: &mut [Operation<'_>],
1571    ) -> Result<(), EspError> {
1572        core::pin::pin!(self.run_async(operations.iter_mut().map(copy_operation))).await
1573    }
1574
1575    pub fn read(&mut self, read: &mut [u8]) -> Result<(), EspError> {
1576        self.transaction(&mut [Operation::Read(read)])
1577    }
1578
1579    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
1580    pub async fn read_async(&mut self, read: &mut [u8]) -> Result<(), EspError> {
1581        let mut operation = [Operation::Read(read)];
1582        let work = core::pin::pin!(self.transaction_async(&mut operation));
1583        work.await
1584    }
1585
1586    pub fn write(&mut self, write: &[u8]) -> Result<(), EspError> {
1587        self.transaction(&mut [Operation::Write(write)])
1588    }
1589
1590    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
1591    pub async fn write_async(&mut self, write: &[u8]) -> Result<(), EspError> {
1592        let mut operation = [Operation::Write(write)];
1593        let work = core::pin::pin!(self.transaction_async(&mut operation));
1594        work.await
1595    }
1596
1597    pub fn transfer_in_place(&mut self, buf: &mut [u8]) -> Result<(), EspError> {
1598        self.transaction(&mut [Operation::TransferInPlace(buf)])
1599    }
1600
1601    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
1602    pub async fn transfer_in_place_async(&mut self, buf: &mut [u8]) -> Result<(), EspError> {
1603        let mut operation = [Operation::TransferInPlace(buf)];
1604        let work = core::pin::pin!(self.transaction_async(&mut operation));
1605        work.await
1606    }
1607
1608    pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), EspError> {
1609        self.transaction(&mut [Operation::Transfer(read, write)])
1610    }
1611
1612    #[cfg(not(esp_idf_spi_master_isr_in_iram))]
1613    pub async fn transfer_async(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), EspError> {
1614        let mut operation = [Operation::Transfer(read, write)];
1615        let work = core::pin::pin!(self.transaction_async(&mut operation));
1616        work.await
1617    }
1618
1619    fn run<'a>(
1620        &mut self,
1621        operations: impl Iterator<Item = Operation<'a>> + 'a,
1622    ) -> Result<(), EspError> {
1623        let cs_pin = CsCtl::Software {
1624            cs: &mut self.cs_pin,
1625            pre_delay: self.pre_delay_us,
1626            post_delay: self.post_delay_us,
1627        };
1628
1629        self.shared_device
1630            .borrow()
1631            .lock(move |device| device.run(cs_pin, operations))
1632    }
1633
1634    #[allow(dead_code)]
1635    async fn run_async<'a>(
1636        &mut self,
1637        operations: impl Iterator<Item = Operation<'a>> + 'a,
1638    ) -> Result<(), EspError> {
1639        let cs_pin = CsCtl::Software {
1640            cs: &mut self.cs_pin,
1641            pre_delay: self.pre_delay_us,
1642            post_delay: self.post_delay_us,
1643        };
1644
1645        let device = self.shared_device.borrow();
1646
1647        let _async_guard = device.async_lock.lock().await;
1648        let _guard = device.lock.enter();
1649
1650        let driver = unsafe { device.driver_mut() };
1651
1652        driver.run_async(cs_pin, operations).await
1653    }
1654}
1655
1656impl<'d, DEVICE, DRIVER> embedded_hal::spi::ErrorType for SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER>
1657where
1658    DEVICE: Borrow<SpiSharedDeviceDriver<'d, DRIVER>> + 'd,
1659    DRIVER: Borrow<SpiDriver<'d>> + 'd,
1660{
1661    type Error = SpiError;
1662}
1663
1664impl<'d, DEVICE, DRIVER> SpiDevice for SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER>
1665where
1666    DEVICE: Borrow<SpiSharedDeviceDriver<'d, DRIVER>> + 'd,
1667    DRIVER: Borrow<SpiDriver<'d>> + 'd,
1668{
1669    fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
1670        Self::read(self, buf).map_err(to_spi_err)
1671    }
1672
1673    fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
1674        Self::write(self, buf).map_err(to_spi_err)
1675    }
1676
1677    fn transaction(
1678        &mut self,
1679        operations: &mut [embedded_hal::spi::Operation<'_, u8>],
1680    ) -> Result<(), Self::Error> {
1681        self.run(operations.iter_mut().map(copy_ehal_operation))
1682            .map_err(to_spi_err)
1683    }
1684}
1685
1686#[cfg(not(esp_idf_spi_master_isr_in_iram))]
1687impl<'d, DEVICE, DRIVER> embedded_hal_async::spi::SpiDevice
1688    for SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER>
1689where
1690    DEVICE: Borrow<SpiSharedDeviceDriver<'d, DRIVER>> + 'd,
1691    DRIVER: Borrow<SpiDriver<'d>> + 'd,
1692{
1693    async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
1694        Self::read_async(self, buf).await.map_err(to_spi_err)
1695    }
1696
1697    async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
1698        Self::write_async(self, buf).await.map_err(to_spi_err)
1699    }
1700
1701    async fn transaction(
1702        &mut self,
1703        operations: &mut [embedded_hal::spi::Operation<'_, u8>],
1704    ) -> Result<(), Self::Error> {
1705        core::pin::pin!(self.run_async(operations.iter_mut().map(copy_ehal_operation)))
1706            .await
1707            .map_err(to_spi_err)
1708    }
1709}
1710
1711fn to_spi_err(err: EspError) -> SpiError {
1712    SpiError::other(err)
1713}
1714
1715// Limit to 64, as we sometimes allocate a buffer of size TRANS_LEN on the stack, so we have to keep it small
1716// SOC_SPI_MAXIMUM_BUFFER_SIZE equals 64 or 72 (esp32s2) anyway
1717const TRANS_LEN: usize = if SOC_SPI_MAXIMUM_BUFFER_SIZE < 64_u32 {
1718    SOC_SPI_MAXIMUM_BUFFER_SIZE as _
1719} else {
1720    64_usize
1721};
1722
1723// Whilst ESP-IDF doesn't have a documented maximum for queued transactions, we need a compile time
1724// max to be able to place the transactions on the stack (without recursion hacks) and not be
1725// forced to use box. Perhaps this is something the user can inject in, via generics or slice.
1726// This means a spi_device_interface_config_t.queue_size higher than this constant will be clamped
1727// down in practice.
1728const MAX_QUEUED_TRANSACTIONS: usize = 6;
1729
1730struct BusLock(spi_device_handle_t);
1731
1732impl BusLock {
1733    fn new(device: spi_device_handle_t) -> Result<Self, EspError> {
1734        esp!(unsafe { spi_device_acquire_bus(device, BLOCK) })?;
1735
1736        Ok(Self(device))
1737    }
1738}
1739
1740impl Drop for BusLock {
1741    fn drop(&mut self) {
1742        unsafe {
1743            spi_device_release_bus(self.0);
1744        }
1745    }
1746}
1747
1748enum CsCtl<'c, 'p, M>
1749where
1750    M: OutputMode,
1751{
1752    Hardware {
1753        enabled: bool,
1754        transactions_count: usize,
1755        last_transaction: Option<usize>,
1756    },
1757    Software {
1758        cs: &'c mut PinDriver<'p, M>,
1759        pre_delay: Option<u32>,
1760        post_delay: Option<u32>,
1761    },
1762}
1763
1764impl<M> CsCtl<'_, '_, M>
1765where
1766    M: OutputMode,
1767{
1768    fn needs_bus_lock(&self) -> bool {
1769        match self {
1770            Self::Hardware {
1771                transactions_count, ..
1772            } => *transactions_count > 1,
1773            Self::Software { .. } => true,
1774        }
1775    }
1776
1777    fn raise_cs(&mut self) -> Result<(), EspError> {
1778        if let CsCtl::Software { cs, pre_delay, .. } = self {
1779            cs.toggle()?;
1780
1781            // TODO: Need to wait asnchronously if in async mode
1782            if let Some(delay) = pre_delay {
1783                Ets::delay_us(*delay);
1784            }
1785        }
1786
1787        Ok(())
1788    }
1789
1790    fn lower_cs(&mut self) -> Result<(), EspError> {
1791        if let CsCtl::Software { cs, post_delay, .. } = self {
1792            cs.toggle()?;
1793
1794            // TODO: Need to wait asnchronously if in async mode
1795            if let Some(delay) = post_delay {
1796                Ets::delay_us(*delay);
1797            }
1798        }
1799
1800        Ok(())
1801    }
1802
1803    fn configure(&self, operation: &mut SpiOperation, index: usize) {
1804        if let SpiOperation::Transaction(transaction) = operation {
1805            self.configure_transaction(transaction, index)
1806        }
1807    }
1808
1809    fn configure_transaction(&self, transaction: &mut spi_transaction_t, index: usize) {
1810        if let Self::Hardware {
1811            enabled,
1812            last_transaction,
1813            ..
1814        } = self
1815        {
1816            set_keep_cs_active(transaction, *enabled && Some(index) != *last_transaction);
1817        }
1818    }
1819}
1820
1821fn spi_operations<'a>(
1822    operations: impl Iterator<Item = Operation<'a>> + 'a,
1823    chunk_size: usize,
1824    duplex: Duplex,
1825) -> impl Iterator<Item = SpiOperation> + 'a {
1826    enum OperationsIter<R, W, T, I, D> {
1827        Read(R),
1828        Write(W),
1829        Transfer(T),
1830        TransferInPlace(I),
1831        Delay(D),
1832    }
1833
1834    impl<R, W, T, I, D> Iterator for OperationsIter<R, W, T, I, D>
1835    where
1836        R: Iterator<Item = SpiOperation>,
1837        W: Iterator<Item = SpiOperation>,
1838        T: Iterator<Item = SpiOperation>,
1839        I: Iterator<Item = SpiOperation>,
1840        D: Iterator<Item = SpiOperation>,
1841    {
1842        type Item = SpiOperation;
1843
1844        fn next(&mut self) -> Option<Self::Item> {
1845            match self {
1846                Self::Read(iter) => iter.next(),
1847                Self::Write(iter) => iter.next(),
1848                Self::Transfer(iter) => iter.next(),
1849                Self::TransferInPlace(iter) => iter.next(),
1850                Self::Delay(iter) => iter.next(),
1851            }
1852        }
1853    }
1854
1855    operations.flat_map(move |op| match op {
1856        Operation::Read(words) => OperationsIter::Read(
1857            spi_read_transactions(words, chunk_size, duplex, LineWidth::Single)
1858                .map(SpiOperation::Transaction),
1859        ),
1860        Operation::ReadWithWidth(words, line_width) => OperationsIter::Read(
1861            spi_read_transactions(words, chunk_size, duplex, line_width)
1862                .map(SpiOperation::Transaction),
1863        ),
1864        Operation::Write(words) => OperationsIter::Write(
1865            spi_write_transactions(words, chunk_size, LineWidth::Single)
1866                .map(SpiOperation::Transaction),
1867        ),
1868        Operation::WriteWithWidth(words, line_width) => OperationsIter::Write(
1869            spi_write_transactions(words, chunk_size, line_width).map(SpiOperation::Transaction),
1870        ),
1871        Operation::Transfer(read, write) => OperationsIter::Transfer(
1872            spi_transfer_transactions(read, write, chunk_size, duplex)
1873                .map(SpiOperation::Transaction),
1874        ),
1875        Operation::TransferInPlace(words) => OperationsIter::TransferInPlace(
1876            spi_transfer_in_place_transactions(words, chunk_size).map(SpiOperation::Transaction),
1877        ),
1878        Operation::DelayNs(delay) => {
1879            OperationsIter::Delay(core::iter::once(SpiOperation::Delay(delay)))
1880        }
1881    })
1882}
1883
1884fn spi_read_transactions(
1885    words: &mut [u8],
1886    chunk_size: usize,
1887    duplex: Duplex,
1888    line_width: LineWidth,
1889) -> impl Iterator<Item = spi_transaction_t> + '_ {
1890    words.chunks_mut(chunk_size).map(move |chunk| {
1891        spi_create_transaction(
1892            chunk.as_mut_ptr(),
1893            core::ptr::null(),
1894            if duplex == Duplex::Full {
1895                chunk.len()
1896            } else {
1897                0
1898            },
1899            chunk.len(),
1900            line_width,
1901        )
1902    })
1903}
1904
1905fn spi_write_transactions(
1906    words: &[u8],
1907    chunk_size: usize,
1908    line_width: LineWidth,
1909) -> impl Iterator<Item = spi_transaction_t> + '_ {
1910    words.chunks(chunk_size).map(move |chunk| {
1911        spi_create_transaction(
1912            core::ptr::null_mut(),
1913            chunk.as_ptr(),
1914            chunk.len(),
1915            0,
1916            line_width,
1917        )
1918    })
1919}
1920
1921fn spi_transfer_in_place_transactions(
1922    words: &mut [u8],
1923    chunk_size: usize,
1924) -> impl Iterator<Item = spi_transaction_t> + '_ {
1925    words.chunks_mut(chunk_size).map(|chunk| {
1926        spi_create_transaction(
1927            chunk.as_mut_ptr(),
1928            chunk.as_mut_ptr(),
1929            chunk.len(),
1930            chunk.len(),
1931            LineWidth::Single,
1932        )
1933    })
1934}
1935
1936fn spi_transfer_transactions<'a>(
1937    read: &'a mut [u8],
1938    write: &'a [u8],
1939    chunk_size: usize,
1940    duplex: Duplex,
1941) -> impl Iterator<Item = spi_transaction_t> + 'a {
1942    enum OperationsIter<E, R, W> {
1943        Equal(E),
1944        ReadLonger(R),
1945        WriteLonger(W),
1946    }
1947
1948    impl<E, R, W> Iterator for OperationsIter<E, R, W>
1949    where
1950        E: Iterator<Item = spi_transaction_t>,
1951        R: Iterator<Item = spi_transaction_t>,
1952        W: Iterator<Item = spi_transaction_t>,
1953    {
1954        type Item = spi_transaction_t;
1955
1956        fn next(&mut self) -> Option<Self::Item> {
1957            match self {
1958                Self::Equal(iter) => iter.next(),
1959                Self::ReadLonger(iter) => iter.next(),
1960                Self::WriteLonger(iter) => iter.next(),
1961            }
1962        }
1963    }
1964
1965    match read.len().cmp(&write.len()) {
1966        Ordering::Equal => {
1967            OperationsIter::Equal(spi_transfer_equal_transactions(read, write, chunk_size))
1968        }
1969        Ordering::Greater => {
1970            let (read, read_trail) = read.split_at_mut(write.len());
1971
1972            OperationsIter::ReadLonger(
1973                spi_transfer_equal_transactions(read, write, chunk_size).chain(
1974                    spi_read_transactions(read_trail, chunk_size, duplex, LineWidth::Single),
1975                ),
1976            )
1977        }
1978        Ordering::Less => {
1979            let (write, write_trail) = write.split_at(read.len());
1980
1981            OperationsIter::WriteLonger(
1982                spi_transfer_equal_transactions(read, write, chunk_size).chain(
1983                    spi_write_transactions(write_trail, chunk_size, LineWidth::Single),
1984                ),
1985            )
1986        }
1987    }
1988}
1989
1990fn spi_transfer_equal_transactions<'a>(
1991    read: &'a mut [u8],
1992    write: &'a [u8],
1993    chunk_size: usize,
1994) -> impl Iterator<Item = spi_transaction_t> + 'a {
1995    read.chunks_mut(chunk_size)
1996        .zip(write.chunks(chunk_size))
1997        .map(|(read_chunk, write_chunk)| {
1998            spi_create_transaction(
1999                read_chunk.as_mut_ptr(),
2000                write_chunk.as_ptr(),
2001                max(read_chunk.len(), write_chunk.len()),
2002                read_chunk.len(),
2003                LineWidth::Single,
2004            )
2005        })
2006}
2007
2008// These parameters assume full duplex.
2009fn spi_create_transaction(
2010    read: *mut u8,
2011    write: *const u8,
2012    transaction_length: usize,
2013    rx_length: usize,
2014    line_width: LineWidth,
2015) -> spi_transaction_t {
2016    let flags = match line_width {
2017        LineWidth::Single => 0,
2018        LineWidth::Dual => SPI_TRANS_MODE_DIO,
2019        LineWidth::Quad => SPI_TRANS_MODE_QIO,
2020    };
2021    spi_transaction_t {
2022        flags,
2023        __bindgen_anon_1: spi_transaction_t__bindgen_ty_1 {
2024            tx_buffer: write as *const _,
2025        },
2026        __bindgen_anon_2: spi_transaction_t__bindgen_ty_2 {
2027            rx_buffer: read as *mut _,
2028        },
2029        length: (transaction_length * 8) as _,
2030        rxlength: (rx_length * 8) as _,
2031        ..Default::default()
2032    }
2033}
2034
2035fn set_keep_cs_active(transaction: &mut spi_transaction_t, _keep_cs_active: bool) {
2036    if _keep_cs_active {
2037        transaction.flags |= SPI_TRANS_CS_KEEP_ACTIVE
2038    }
2039}
2040
2041fn spi_transmit(
2042    handle: spi_device_handle_t,
2043    transactions: impl Iterator<Item = spi_transaction_t>,
2044    polling: bool,
2045    queue_size: usize,
2046) -> Result<(), EspError> {
2047    if polling {
2048        for mut transaction in transactions {
2049            esp!(unsafe { spi_device_polling_transmit(handle, &mut transaction as *mut _) })?;
2050        }
2051    } else {
2052        pub type Queue = Deque<spi_transaction_t, MAX_QUEUED_TRANSACTIONS>;
2053
2054        let mut queue = Queue::new();
2055        let queue_size = min(MAX_QUEUED_TRANSACTIONS, queue_size);
2056
2057        let push = |queue: &mut Queue, transaction| {
2058            let _ = queue.push_back(transaction);
2059            esp!(unsafe { spi_device_queue_trans(handle, queue.back_mut().unwrap(), delay::BLOCK) })
2060        };
2061
2062        let pop = |queue: &mut Queue| {
2063            let mut rtrans = ptr::null_mut();
2064            esp!(unsafe { spi_device_get_trans_result(handle, &mut rtrans, delay::BLOCK) })?;
2065
2066            if rtrans != queue.front_mut().unwrap() {
2067                unreachable!();
2068            }
2069            queue.pop_front().unwrap();
2070
2071            Ok(())
2072        };
2073
2074        let pop_all = |queue: &mut Queue| {
2075            while !queue.is_empty() {
2076                pop(queue)?;
2077            }
2078
2079            Ok(())
2080        };
2081
2082        for transaction in transactions {
2083            if queue.len() == queue_size {
2084                // If the queue is full, we wait for the first transaction in the queue
2085                pop(&mut queue)?;
2086            }
2087
2088            // Write transaction to a stable memory location
2089            push(&mut queue, transaction)?;
2090        }
2091
2092        pop_all(&mut queue)?;
2093    }
2094
2095    Ok(())
2096}
2097
2098#[allow(dead_code)]
2099async fn spi_transmit_async(
2100    handle: spi_device_handle_t,
2101    transactions: impl Iterator<Item = spi_transaction_t>,
2102    queue_size: usize,
2103) -> Result<(), EspError> {
2104    pub type Queue = Deque<(spi_transaction_t, HalIsrNotification), MAX_QUEUED_TRANSACTIONS>;
2105
2106    let mut queue = Queue::new();
2107    let queue = &mut queue;
2108
2109    let queue_size = min(MAX_QUEUED_TRANSACTIONS, queue_size);
2110    let queued = Cell::new(0_usize);
2111
2112    let fut = &mut core::pin::pin!(async {
2113        let push = |queue: &mut Queue, transaction| {
2114            queue
2115                .push_back((transaction, HalIsrNotification::new()))
2116                .map_err(|_| EspError::from_infallible::<{ ESP_ERR_INVALID_STATE }>())?;
2117            queued.set(queue.len());
2118
2119            let last = queue.back_mut().unwrap();
2120            last.0.user = &last.1 as *const _ as *mut _;
2121            match esp!(unsafe { spi_device_queue_trans(handle, &mut last.0, delay::BLOCK) }) {
2122                Err(e) if e.code() == ESP_ERR_TIMEOUT => unreachable!(),
2123                other => other,
2124            }
2125        };
2126
2127        let pop = |queue: &mut Queue, delay: TickType_t| {
2128            let mut rtrans = ptr::null_mut();
2129            match esp!(unsafe { spi_device_get_trans_result(handle, &mut rtrans, delay) }) {
2130                Err(e) if e.code() == ESP_ERR_TIMEOUT => return Ok(false),
2131                Err(e) => Err(e)?,
2132                Ok(()) => (),
2133            };
2134
2135            if rtrans != &mut queue.front_mut().unwrap().0 {
2136                unreachable!();
2137            }
2138
2139            queue.pop_front().unwrap();
2140            queued.set(queue.len());
2141
2142            Ok(true)
2143        };
2144
2145        // NOTE: after a successful `wait().await`, the front transaction result MUST be
2146        // fetched with a blocking `pop`: the SPI ISR invokes the post-transaction callback
2147        // (which fires the notification we just awaited) *before* it posts the result to
2148        // the driver return queue (see `spi_intr` in ESP-IDF's `spi_master.c`:
2149        // `spi_post_trans` runs before `xQueueSendFromISR`). On multi-core chips the
2150        // awoken task can thus observe the notification before the result is visible. As
2151        // the wait consumed the notification, going back to a non-blocking `pop` + `wait`
2152        // would hang forever. The blocking fetch is safe: the ISR posts the result right
2153        // after the callback.
2154
2155        for transaction in transactions {
2156            // If the queue is full, we wait for the first transaction in the queue
2157            while queue.len() == queue_size {
2158                if !pop(queue, delay::NON_BLOCK)? {
2159                    queue.front_mut().unwrap().1.wait().await;
2160                    pop(queue, delay::BLOCK)?;
2161                }
2162            }
2163
2164            // Write transaction to a stable memory location
2165            push(queue, transaction)?;
2166        }
2167
2168        while !queue.is_empty() {
2169            if !pop(queue, delay::NON_BLOCK)? {
2170                queue.front_mut().unwrap().1.wait().await;
2171                pop(queue, delay::BLOCK)?;
2172            }
2173        }
2174
2175        Ok(())
2176    });
2177
2178    with_completion(fut, |completed| {
2179        if !completed {
2180            for _ in 0..queued.get() {
2181                let mut rtrans = ptr::null_mut();
2182                esp!(unsafe { spi_device_get_trans_result(handle, &mut rtrans, delay::BLOCK) })
2183                    .unwrap();
2184            }
2185        }
2186    })
2187    .await
2188}
2189
2190extern "C" fn spi_notify(transaction: *mut spi_transaction_t) {
2191    if let Some(transaction) = unsafe { transaction.as_ref() } {
2192        if let Some(notification) = unsafe {
2193            (transaction.user as *mut HalIsrNotification as *const HalIsrNotification).as_ref()
2194        } {
2195            notification.notify_lsb();
2196        }
2197    }
2198}
2199
2200fn copy_operation<'b>(operation: &'b mut Operation<'_>) -> Operation<'b> {
2201    match operation {
2202        Operation::Read(read) => Operation::Read(read),
2203        Operation::ReadWithWidth(read, line_width) => Operation::ReadWithWidth(read, *line_width),
2204        Operation::Write(write) => Operation::Write(write),
2205        Operation::WriteWithWidth(write, line_width) => {
2206            Operation::WriteWithWidth(write, *line_width)
2207        }
2208        Operation::Transfer(read, write) => Operation::Transfer(read, write),
2209        Operation::TransferInPlace(write) => Operation::TransferInPlace(write),
2210        Operation::DelayNs(delay) => Operation::DelayNs(*delay),
2211    }
2212}
2213
2214fn copy_ehal_operation<'b>(
2215    operation: &'b mut embedded_hal::spi::Operation<'_, u8>,
2216) -> Operation<'b> {
2217    match operation {
2218        embedded_hal::spi::Operation::Read(read) => Operation::Read(read),
2219        embedded_hal::spi::Operation::Write(write) => Operation::Write(write),
2220        embedded_hal::spi::Operation::Transfer(read, write) => Operation::Transfer(read, write),
2221        embedded_hal::spi::Operation::TransferInPlace(write) => Operation::TransferInPlace(write),
2222        embedded_hal::spi::Operation::DelayNs(delay) => Operation::DelayNs(*delay),
2223    }
2224}
2225
2226#[allow(dead_code)]
2227async fn with_completion<F, D>(fut: F, dtor: D) -> F::Output
2228where
2229    F: Future,
2230    D: FnMut(bool),
2231{
2232    struct Completion<D>
2233    where
2234        D: FnMut(bool),
2235    {
2236        dtor: D,
2237        completed: bool,
2238    }
2239
2240    impl<D> Drop for Completion<D>
2241    where
2242        D: FnMut(bool),
2243    {
2244        fn drop(&mut self) {
2245            (self.dtor)(self.completed);
2246        }
2247    }
2248
2249    let mut completion = Completion {
2250        dtor,
2251        completed: false,
2252    };
2253
2254    let result = fut.await;
2255
2256    completion.completed = true;
2257
2258    result
2259}
2260
2261macro_rules! impl_spi {
2262    ($spi:ident: $device:expr) => {
2263        crate::impl_peripheral!($spi);
2264
2265        impl Spi for $spi<'_> {
2266            #[inline(always)]
2267            fn device() -> spi_host_device_t {
2268                $device
2269            }
2270        }
2271    };
2272}
2273
2274macro_rules! impl_spi_any_pins {
2275    ($spi:ident) => {
2276        impl SpiAnyPins for $spi<'_> {}
2277    };
2278}
2279
2280impl_spi!(SPI1: spi_host_device_t_SPI1_HOST);
2281impl_spi!(SPI2: spi_host_device_t_SPI2_HOST);
2282#[cfg(any(esp32, esp32s2, esp32s3, esp32p4))]
2283impl_spi!(SPI3: spi_host_device_t_SPI3_HOST);
2284
2285impl_spi_any_pins!(SPI2);
2286#[cfg(any(esp32, esp32s2, esp32s3, esp32p4))]
2287impl_spi_any_pins!(SPI3);