sdmmc-core 0.5.0

SD/MMC core data structures and algorithms
Documentation
use crate::lib_enum;
use crate::result::{Error, Result};

use super::{ExtCsd, ExtCsdIndex, ModeConfig};

mod ffu;

pub use ffu::*;

/// Represents the variants for the `MODE_OPERATION_CODES` field of the [ExtCsd] register.
///
/// # Note
///
/// Values depend on the mode configured in the `MODE_CONFIG` field.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ModeOperationCodes {
    Normal(u8),
    Ffu(FfuOperationCodes),
    Vendor(u8),
}

impl ModeOperationCodes {
    /// Creates a new [ModeOperationCodes].
    pub const fn new() -> Self {
        Self::Normal(0)
    }

    /// Gets the [ModeConfig] for the [ModeOperationCodes].
    pub const fn mode_config(&self) -> ModeConfig {
        match self {
            Self::Normal(_) => ModeConfig::Normal,
            Self::Ffu(_) => ModeConfig::Ffu,
            Self::Vendor(_) => ModeConfig::Vendor,
        }
    }

    /// Attempts to convert a [ModeConfig] and code into a [ModeOperationCodes].
    pub const fn try_from_mode_config(mode: ModeConfig, code: u8) -> Result<Self> {
        match mode {
            ModeConfig::Normal => Ok(ModeOperationCodes::Normal(code)),
            ModeConfig::Ffu => match FfuOperationCodes::from_raw(code) {
                Ok(c) => Ok(ModeOperationCodes::Ffu(c)),
                Err(err) => Err(err),
            },
            ModeConfig::Vendor => Ok(ModeOperationCodes::Vendor(code)),
        }
    }

    /// Converts the [ModeOperationCodes] into a [`u8`].
    pub const fn into_inner(self) -> u8 {
        match self {
            Self::Normal(code) => code,
            Self::Ffu(ffu) => ffu.into_raw(),
            Self::Vendor(code) => code,
        }
    }
}

impl Default for ModeOperationCodes {
    fn default() -> Self {
        Self::new()
    }
}

impl ExtCsd {
    /// Gets the `MODE_OPERATION_CODES` field of the [ExtCsd] register.
    pub const fn mode_operation_codes(&self) -> Result<ModeOperationCodes> {
        let code = self.0[ExtCsdIndex::ModeOperationCodes.into_inner()];

        match self.mode_config() {
            Ok(mode) => ModeOperationCodes::try_from_mode_config(mode, code),
            Err(err) => Err(err),
        }
    }

    /// Sets the `MODE_OPERATION_CODES` field of the [ExtCsd] register.
    ///
    /// # Note
    ///
    /// Also sets the `MODE_CONFIG` field based on the [ModeOperationCodes].
    pub fn set_mode_operation_codes(&mut self, val: ModeOperationCodes) {
        self.set_mode_config(val.mode_config());
        self.0[ExtCsdIndex::ModeOperationCodes.into_inner()] = val.into_inner();
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_mode_operation_codes() {
        let mut ext_csd = ExtCsd::new();

        assert_eq!(
            ext_csd.mode_operation_codes(),
            Ok(ModeOperationCodes::new())
        );

        [ModeConfig::Normal, ModeConfig::Ffu, ModeConfig::Vendor]
            .into_iter()
            .for_each(|mode| {
                (0..=u8::MAX).for_each(|code| {
                    match ModeOperationCodes::try_from_mode_config(mode, code) {
                        Ok(c) => {
                            ext_csd.set_mode_operation_codes(c);
                            assert_eq!(ext_csd.mode_config(), Ok(c.mode_config()));
                            assert_eq!(ext_csd.mode_operation_codes(), Ok(c));
                        }
                        Err(err) => {
                            // only invalid FFU codes return an error
                            assert_eq!(mode, ModeConfig::Ffu);
                            assert_eq!(Err(err), FfuOperationCodes::from_raw(code));
                        }
                    }
                });
            });
    }
}