lilkaoxide 0.1.1

Rust support library for Lilka console
use crate::spi::{InnerDelay, SpiBusWrapper, SpiInitError};
use esp_hal::gpio::{Level, Output, OutputConfig};
use esp_hal::peripherals::GPIO16;

use sdmmc;
use embedded_timers::{clock::Clock, instant::TimespecInstant};

pub struct SdDelay(pub InnerDelay);

#[cfg(feature = "blocking")]
impl embedded_hal::delay::DelayNs for SdDelay {
    fn delay_ns(&mut self, ns: u32) {
        self.0.delay_ns(ns)
    }
}

#[cfg(feature = "async")]
impl sdmmc::delay::Delay for SdDelay {
    type Future = embassy_time::Timer;

    fn delay_ms(&mut self, ms: u32) -> Self::Future {
        let _ = &mut self.0;
        embassy_time::Timer::after_millis(ms as u64)
    }
}

pub struct SdClock;

impl Clock for SdClock {
    type Instant = TimespecInstant;

    fn now(&self) -> Self::Instant {
        let now = embassy_time::Instant::now().as_millis();
        TimespecInstant::new((now / 1000) as u32, ((now % 1000) * 1_000_000) as u32)
    }
}

pub struct SdSpi<'a>(pub &'a SpiBusWrapper);

pub type SdBus<'a> = sdmmc::bus::spi::Bus<SdSpi<'a>, Output<'a>, SdClock>;
pub type SDC<'a> = sdmmc::SD<SdBus<'a>>;
pub type SdVolMgr<'a> = SDC<'a>;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SdInitError {
    SpiDevice(SpiInitError),
    CardInitFailed,
}

#[cfg(feature = "AtomicBus")]
compile_error!("AtomicBus is not supported with async-sdmmc");

impl SdInitError {
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::SpiDevice(err) => err.as_str(),
            Self::CardInitFailed => "SD card init failed",
        }
    }
}

#[cfg(feature = "blocking")]
impl<'a> sdmmc::bus::spi::Transfer for SdSpi<'a> {
    type Error = SpiInitError;

    fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
        #[cfg(feature = "RefCellBus")]
        {
            let bus = &mut *self.0.borrow_mut();
            match (!tx.is_empty(), !rx.is_empty()) {
                (true, true) => {
                    let n = core::cmp::min(tx.len(), rx.len());
                    rx[..n].copy_from_slice(&tx[..n]);
                    embedded_hal::spi::SpiBus::transfer_in_place(bus, &mut rx[..n])
                        .map_err(|_| SpiInitError::DeviceInitFailed)?;
                    Ok(())
                }
                (true, false) => embedded_hal::spi::SpiBus::write(bus, tx)
                    .map_err(|_| SpiInitError::DeviceInitFailed),
                (false, true) => embedded_hal::spi::SpiBus::read(bus, rx)
                    .map_err(|_| SpiInitError::DeviceInitFailed),
                _ => Ok(()),
            }
        }
        #[cfg(feature = "CriticalSectionBus")]
        {
            self.0.lock(|bus| {
                let mut bus = bus.borrow_mut();
                match (!tx.is_empty(), !rx.is_empty()) {
                    (true, true) => {
                        let n = core::cmp::min(tx.len(), rx.len());
                        rx[..n].copy_from_slice(&tx[..n]);
                        embedded_hal::spi::SpiBus::transfer_in_place(&mut *bus, &mut rx[..n])
                            .map_err(|_| SpiInitError::DeviceInitFailed)?;
                        Ok(())
                    }
                    (true, false) => embedded_hal::spi::SpiBus::write(&mut *bus, tx)
                        .map_err(|_| SpiInitError::DeviceInitFailed),
                    (false, true) => embedded_hal::spi::SpiBus::read(&mut *bus, rx)
                        .map_err(|_| SpiInitError::DeviceInitFailed),
                    _ => Ok(()),
                }
            })
        }
    }
}

#[cfg(feature = "async")]
impl<'a> sdmmc::bus::spi::Transfer for SdSpi<'a> {
    type Error = SpiInitError;

    async fn transfer(&mut self, tx: &[u8], rx: &mut [u8]) -> Result<(), Self::Error> {
        let mut guard = self.0.lock().await;
        match (!tx.is_empty(), !rx.is_empty()) {
            (true, true) => {
                let n = core::cmp::min(tx.len(), rx.len());
                rx[..n].copy_from_slice(&tx[..n]);
                embedded_hal_async::spi::SpiBus::transfer_in_place(&mut *guard, &mut rx[..n])
                    .await
                    .map_err(|_| SpiInitError::DeviceInitFailed)?;
                Ok(())
            }
            (true, false) => embedded_hal_async::spi::SpiBus::write(&mut *guard, tx)
                .await
                .map_err(|_| SpiInitError::DeviceInitFailed),
            (false, true) => embedded_hal_async::spi::SpiBus::read(&mut *guard, rx)
                .await
                .map_err(|_| SpiInitError::DeviceInitFailed),
            _ => Ok(()),
        }
    }
}

#[cfg_attr(not(feature = "async"), deasync::deasync)]
pub async fn init_sd<'a>(
    spi_bus: &'a SpiBusWrapper,
    sd_cs: GPIO16<'a>,
    delay: SdDelay,
) -> Result<SDC<'a>, SdInitError> {
    let sd_cs = Output::new(sd_cs, Level::Low, OutputConfig::default());
    let mut bus = SdBus::new(SdSpi(spi_bus), sd_cs, SdClock);
    let card = bus.init(delay).await.map_err(|_| SdInitError::CardInitFailed)?;
    let sd = sdmmc::SD::init(bus, card)
        .await
        .map_err(|_| SdInitError::CardInitFailed)?;

    Ok(sd)
}