1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use crate::sys::*;

use core::{marker::PhantomData, ops::Deref};

use esp_idf_hal::{
    gpio::{InputPin, OutputPin},
    peripheral::Peripheral,
    spi::SpiDriver,
};

pub struct SpiDevice<'d> {
    configuration: sdspi_device_config_t,
    driver: SpiDriver<'d>,
    _p: PhantomData<&'d mut ()>,
}

impl Drop for SpiDevice<'_> {
    fn drop(&mut self) {
        let result = unsafe { spi_bus_free(self.get_host()) };

        if result != ESP_OK {
            panic!("Failed to remove SPI device");
        }
    }
}

impl<'d> SpiDevice<'d> {
    pub fn new(
        driver: SpiDriver<'d>,
        cs: impl Peripheral<P = impl OutputPin> + 'd,
        cd: Option<impl Peripheral<P = impl InputPin> + 'd>,
        wp: Option<impl Peripheral<P = impl InputPin> + 'd>,
        int: Option<impl Peripheral<P = impl InputPin> + 'd>,
        #[cfg(not(any(
            esp_idf_version_major = "4",
            all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
            all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
        )))] // For ESP-IDF v5.2 and later
        wp_polarity: Option<bool>,
    ) -> Self {
        let configuration = sdspi_device_config_t {
            host_id: driver.host(),
            gpio_cs: cs.into_ref().deref().pin(),
            gpio_cd: cd.map(|cd| cd.into_ref().deref().pin()).unwrap_or(-1),
            gpio_wp: wp.map(|wp| wp.into_ref().deref().pin()).unwrap_or(-1),
            gpio_int: int.map(|int| int.into_ref().deref().pin()).unwrap_or(-1),
            #[cfg(not(any(
                esp_idf_version_major = "4",
                all(esp_idf_version_major = "5", esp_idf_version_minor = "0"),
                all(esp_idf_version_major = "5", esp_idf_version_minor = "1"),
            )))] // For ESP-IDF v5.2 and later
            gpio_wp_polarity: wp_polarity.unwrap_or(false), // Active when low
        };

        Self {
            configuration,
            driver,
            _p: PhantomData,
        }
    }

    pub fn get_host(&self) -> spi_host_device_t {
        self.driver.host()
    }

    pub fn set_clock(&mut self, clock: u32) -> Result<(), EspError> {
        esp!(unsafe { sdspi_host_set_card_clk(self.get_host() as i32, clock) })
    }

    pub fn get_clock(&mut self) -> Result<u32, EspError> {
        unimplemented!() // ! : Function not found in `esp-idf-sys`
    }

    pub fn enable_interrupt(&mut self) -> Result<(), EspError> {
        esp!(unsafe { sdspi_host_io_int_enable(self.get_host() as i32) })
    }

    pub fn wait_interrupt(&mut self, timeout: u32) -> Result<(), EspError> {
        esp!(unsafe { sdspi_host_io_int_wait(self.get_host() as i32, timeout) })
    }

    pub(crate) fn get_device_configuration(&self) -> &sdspi_device_config_t {
        &self.configuration
    }
}