sdmmc-core 0.5.0

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

use super::{
    BlockLength, CSD_LEN, CardCommandClass, Copied, DsrImplemented, EraseBlockEnable, FileFormat,
    FileFormatGroup, NsAccessCycles, PermanentWriteProtect, ReadBlockMisalign, ReadBlockPartial,
    SectorSize, TemporaryWriteProtect, TransferSpeed, VddCurrentMax, VddCurrentMin,
    WriteBlockMisalign, WriteBlockPartial, WriteProtectGroupEnable, WriteProtectGroupSize,
    WriteProtectUntilPowerCycle, WriteSpeedFactor,
};

use super::structure::CsdStructure;

pub use super::v2::{TimeAsyncAccessCycles, taac};

mod c_size;

#[cfg(test)]
mod tests;

pub use c_size::CapacitySize;

lib_bitfield! {
    /// Represents the Card-Specific Data (CSD) register V1.0 value returned in a `R2` response in SD mode.
    ///
    /// This is for standard capacity SD memory cards.
    pub CsdV3(MSB0 [u8; CSD_LEN]): u32 {
        /// Represents the raw `CSD_STRUCTURE` field for the [CsdV3].
        raw_csd_structure: 127, 126;
        /// Represents the raw `TAAC` field for the [CsdV3].
        raw_taac: 119, 112;
        /// Represents the raw `NSAC` field for the [CsdV3].
        raw_nsac: 111, 104;
        /// Represents the raw `TRAN_SPEED` field for the [CsdV3].
        raw_tran_speed: 103, 96;
        /// Represents the raw `CCC` field for the [CsdV3].
        raw_ccc: 95, 84;
        /// Represents the raw `READ_BL_LEN` field for the [CsdV3].
        raw_read_bl_len: 83, 80;
        /// Represents the raw `READ_BL_PARTIAL` field for the [CsdV3].
        raw_read_bl_partial: 79;
        /// Represents the raw `WRITE_BLK_MISALIGN` field for the [CsdV3].
        raw_write_blk_misalign: 78;
        /// Represents the raw `READ_BLK_MISALIGN` field for the [CsdV3].
        raw_read_blk_misalign: 77;
        /// Represents the raw `DSR_IMPL` field for the [CsdV3].
        raw_dsr_imp: 76;
        /// Represents the raw `C_SIZE` field for the [CsdV3].
        raw_c_size: 75, 48;
        /// Represents the raw `ERASE_BLK_EN` field for the [CsdV3].
        raw_erase_blk_en: 46;
        /// Represents the raw `SECTOR_SIZE` field for the [CsdV3].
        raw_sector_size: 45, 39;
        /// Represents the raw `WR_GRP_SIZE` field for the [CsdV3].
        raw_wp_grp_size: 38, 32;
        /// Represents the raw `WR_GRP_ENABLE` field for the [CsdV3].
        raw_wp_grp_enable: 31;
        /// Represents the raw `R2W_FACTOR` field for the [CsdV3].
        raw_r2w_factor: 28, 26;
        /// Represents the raw `WRITE_BL_LEN` field for the [CsdV3].
        raw_write_bl_len: 25, 22;
        /// Represents the raw `WRITE_BL_PARTIAL` field for the [CsdV3].
        raw_write_bl_partial: 21;
        /// Represents the raw `FILE_FORMAT_GRP` field for the [CsdV3].
        raw_file_format_grp: 15;
        /// Represents the raw `COPY` field for the [CsdV3].
        raw_copy: 14;
        /// Represents the raw `PERM_WRITE_PROTECT` field for the [CsdV3].
        raw_perm_write_protect: 13;
        /// Represents the raw `TMP_WRITE_PROTECT` field for the [CsdV3].
        raw_tmp_write_protect: 12;
        /// Represents the raw `RAW_FILE_FORMAT` field for the [CsdV3].
        raw_file_format: 11, 10;
        /// Represents the raw `WP_UPC` field for the [CsdV3].
        raw_wp_upc: 9;
        /// Represents the raw `CRC` field for the [CsdV3].
        raw_crc: 7, 1;
        /// Represents the raw `END` field for the [CsdV3].
        raw_end: 0;
    }
}

impl CsdV3 {
    /// Represents the byte length of the [CsdV3].
    pub const LEN: usize = CSD_LEN;
    /// Represents the block length (in bytes) for [CsdV3].
    pub const BLOCK_LEN: usize = 512 * 1024;
    /// Represents the default inner representation of the [CsdV3].
    pub const DEFAULT: [u8; Self::LEN] = [
        0x80, 0x0e, 0x00, 0x32, 0x0b, 0x59, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x0a, 0x40, 0x00,
        0xaf,
    ];

    /// Creates a new [CsdV3].
    pub const fn new() -> Self {
        Self(Self::DEFAULT)
    }

    /// Attempts to get the `CSD_STRUCTURE` field for the [CsdV3].
    pub const fn csd_structure(&self) -> Result<CsdStructure> {
        match CsdStructure::from_raw(self.raw_csd_structure() as u8) {
            Ok(csd @ CsdStructure::V3) => Ok(csd),
            Ok(csd) => Err(Error::invalid_field_variant(
                "csd_structure",
                csd.into_raw() as usize,
            )),
            Err(err) => Err(err),
        }
    }

    /// Attempts to get the `TAAC` field for the [CsdV3].
    ///
    /// The host should not use `TAAC` to calculate timeout, and should use fixed values.
    pub const fn taac(&self) -> Result<TimeAsyncAccessCycles> {
        TimeAsyncAccessCycles::try_from_inner(self.raw_taac() as u8)
    }

    /// Gets the `NSAC` field for the [CsdV3].
    ///
    /// The host should not use `NSAC` to calculate timeout, and should use fixed values.
    pub const fn nsac(&self) -> Result<NsAccessCycles> {
        match self.raw_nsac() {
            nsac @ 0 => Ok(NsAccessCycles::from_bits(nsac as u8)),
            nsac => Err(Error::InvalidVariant(nsac as usize)),
        }
    }

    /// Attempts to get the `TRAN_SPEED` field for the [CsdV3].
    pub const fn tran_speed(&self) -> Result<TransferSpeed> {
        TransferSpeed::try_from_bits(self.raw_tran_speed() as u8)
    }

    /// Gets the `CCC` field for the [CsdV3].
    pub const fn ccc(&self) -> CardCommandClass {
        CardCommandClass::from_bits(self.raw_ccc() as u16)
    }

    /// Attempts to get the `READ_BL_LEN` field for the [CsdV3].
    pub const fn read_bl_len(&self) -> Result<BlockLength> {
        let raw_bl_len = self.raw_read_bl_len() as u8;

        match BlockLength::from_raw(raw_bl_len) {
            Ok(bl @ BlockLength::Bytes512) => Ok(bl),
            _ => Err(Error::invalid_field_variant(
                "read_bl_len",
                raw_bl_len as usize,
            )),
        }
    }

    /// Gets the `READ_BL_PARTIAL` field for the [CsdV3].
    ///
    /// For SD cards, this bit is always set.
    ///
    /// Indicates partial block reads are allowed, down to a minimum one-byte read.
    pub const fn read_bl_partial(&self) -> ReadBlockPartial {
        ReadBlockPartial::from_inner(self.raw_read_bl_partial())
    }

    /// Gets the `WRITE_BLK_MISALIGN` field for the [CsdV3].
    ///
    /// If set, indicates crossing physical block boundaries is allowed.
    /// Otherwise, crossing physical block boundaries is invalid.
    pub const fn write_blk_misalign(&self) -> WriteBlockMisalign {
        WriteBlockMisalign::from_inner(self.raw_write_blk_misalign())
    }

    /// Gets the `READ_BLK_MISALIGN` field for the [CsdV3].
    ///
    /// If set, indicates crossing physical block boundaries is allowed.
    /// Otherwise, crossing physical block boundaries is invalid.
    pub const fn read_blk_misalign(&self) -> ReadBlockMisalign {
        ReadBlockMisalign::from_inner(self.raw_read_blk_misalign())
    }

    /// Gets the `DSR_IMP` field for the [CsdV3].
    ///
    /// Indicates if the configurable driver stage is integrated on the card.
    ///
    /// If set, the `DSR` register is implemented.
    /// Otherwise, the `DSR` register does not exist.
    pub const fn dsr_imp(&self) -> DsrImplemented {
        DsrImplemented::from_inner(self.raw_dsr_imp())
    }

    /// Gets the `C_SIZE` field for the [CsdV3].
    ///
    /// Represents one of the parameters used to calculate the card's total memory capacity.
    pub const fn c_size(&self) -> CapacitySize {
        CapacitySize::from_inner(self.raw_c_size())
    }

    /// Gets the total memory capacity of the SD card.
    ///
    /// The memory capacity is calculated as follows:
    ///
    /// ```no_build,no_run
    /// MEMORY_CAPACITY =  (C_SIZE + 1) * 512KByte
    /// ```
    pub const fn capacity(&self) -> u64 {
        (self.c_size().size() as u64) * (Self::BLOCK_LEN as u64)
    }

    /// Gets the `ERASE_BLK_EN` field for the [CsdV3].
    ///
    /// - If `false` (`0`), the host can erase units of `SECTOR_SIZE`.
    /// - If `true` (`1`), the host can erase units of `512` bytes.
    pub const fn erase_blk_en(&self) -> EraseBlockEnable {
        EraseBlockEnable::from_inner(self.raw_erase_blk_en())
    }

    /// Gets the `SECTOR_SIZE` field for the [CsdV3].
    ///
    /// The value defines the number of write blocks `WRITE_BL_LEN` ([BlockLength]).
    ///
    /// The raw value is a 7-bit binary encoded value.
    ///
    /// The returned value is adjusted for the real value: `RAW_SECTOR_SIZE + 1`.
    pub const fn sector_size(&self) -> Result<SectorSize> {
        match self.raw_sector_size() {
            sector_size @ 0x7f => Ok(SectorSize::from_inner(sector_size as u8)),
            sector_size => Err(Error::invalid_field_variant(
                "sector_size",
                sector_size as usize,
            )),
        }
    }

    /// Gets the `WP_GRP_SIZE` field for the [CsdV3].
    ///
    /// The raw value is a 7-bit binary encoded value.
    ///
    /// The returned value is adjusted for the real value: `RAW_WP_GRP_SIZE + 1`.
    pub const fn wp_grp_size(&self) -> WriteProtectGroupSize {
        WriteProtectGroupSize::from_inner(self.raw_wp_grp_size() as u8)
    }

    /// Gets the `WP_GRP_ENABLE` field for the [CsdV3].
    ///
    /// If unset (`false`, `0`), no group write protection is possible.
    pub const fn wp_grp_enable(&self) -> WriteProtectGroupEnable {
        WriteProtectGroupEnable::from_inner(self.raw_wp_grp_enable())
    }

    /// Attempts to get the `R2W_FACTOR` field for the [CsdV3].
    pub const fn r2w_factor(&self) -> Result<WriteSpeedFactor> {
        let raw_r2w = self.raw_r2w_factor() as u8;

        match WriteSpeedFactor::from_raw(raw_r2w) {
            Ok(r2w @ WriteSpeedFactor::Multiple4) => Ok(r2w),
            _ => Err(Error::invalid_field_variant("r2w_factor", raw_r2w as usize)),
        }
    }

    /// Attempts to get the `WRITE_BL_LEN` field for the [CsdV3].
    pub const fn write_bl_len(&self) -> Result<BlockLength> {
        let raw_bl_len = self.raw_write_bl_len() as u8;

        match BlockLength::from_raw(raw_bl_len) {
            Ok(bl @ BlockLength::Bytes512) => Ok(bl),
            _ => Err(Error::invalid_field_variant(
                "write_bl_len",
                raw_bl_len as usize,
            )),
        }
    }

    /// Gets the `WRITE_BL_PARTIAL` field for the [CsdV3].
    ///
    /// - If unset (`false`, `0`), partial block writes are not possible.
    /// - If set (`true`, `1`), partial block writes are available down to a 1-byte minimum write.
    pub const fn write_bl_partial(&self) -> WriteBlockPartial {
        WriteBlockPartial::from_inner(self.raw_write_bl_partial())
    }

    /// Gets the `FILE_FORMAT_GRP` field for the [CsdV3].
    ///
    /// - If unset (`false`, `0`), the `FILE_FORMAT` field values are defined by [FileFormat].
    /// - If set (`true`, `1`), the `FILE_FORMAT` field values are reserved for future use.
    pub const fn file_format_grp(&self) -> FileFormatGroup {
        FileFormatGroup::from_inner(self.raw_file_format_grp())
    }

    /// Gets the `COPY` field for the [CsdV3].
    ///
    /// - If unset (`false`, `0`), the contents are the original.
    /// - If set (`true`, `1`), the contents have been copied.
    pub const fn copy(&self) -> Copied {
        Copied::from_inner(self.raw_copy())
    }

    /// Gets the `PERM_WRITE_PROTECT` field for the [CsdV3].
    ///
    /// - If unset (`false`, `0`), permanent write protect is disabled.
    /// - If set (`true`, `1`), permanent write protect is enabled.
    pub const fn perm_write_protect(&self) -> PermanentWriteProtect {
        PermanentWriteProtect::from_inner(self.raw_perm_write_protect())
    }

    /// Gets the `TMP_WRITE_PROTECT` field for the [CsdV3].
    ///
    /// - If unset (`false`, `0`), temporary write protect is disabled.
    /// - If set (`true`, `1`), temporary write protect is enabled.
    pub const fn tmp_write_protect(&self) -> TemporaryWriteProtect {
        TemporaryWriteProtect::from_inner(self.raw_tmp_write_protect())
    }

    /// Gets the `WP_UPC` field for the [CsdV3].
    ///
    /// - If unset (`false`, `0`), write protect is disabled.
    /// - If set (`true`, `1`), write protect until power cycle is enabled.
    pub const fn wp_upc(&self) -> WriteProtectUntilPowerCycle {
        WriteProtectUntilPowerCycle::from_inner(self.raw_wp_upc())
    }

    /// Gets the `FILE_FORMAT` field for the [CsdV3].
    ///
    /// - If `FILE_FORMAT_GRP` is unset (`false`, `0`), values are defined by [FileFormat].
    /// - If `FILE_FORMAT_GRP` is set (`true`, `1`), values are reserved for future use.
    pub const fn file_format(&self) -> Result<FileFormat> {
        match self.file_format_grp() {
            FileFormatGroup::FileFormat => FileFormat::from_raw(self.raw_file_format() as u8),
            FileFormatGroup::Reserved => Ok(FileFormat::Unknown),
        }
    }

    /// Gets the `CRC` field of the [CsdV3].
    pub const fn crc(&self) -> Crc7 {
        Crc7::from_bits(self.raw_crc() as u8)
    }

    /// Attempts to convert an inner representation into a [CsdV3].
    pub const fn try_from_inner(val: [u8; Self::LEN]) -> Result<Self> {
        let csd = Self(val);

        const_try!(csd.csd_structure());
        const_try!(csd.taac());
        const_try!(csd.tran_speed());
        const_try!(csd.read_bl_len());
        const_try!(csd.r2w_factor());
        const_try!(csd.write_bl_len());
        const_try!(csd.file_format());

        Ok(csd)
    }

    /// Attempts to convert a byte slice into a [CsdV3].
    pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
        match val.len() {
            len if len < Self::LEN => Err(Error::InvalidVariant(len)),
            _ => Self::try_from_inner([
                val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], val[9],
                val[10], val[11], val[12], val[13], val[14], val[15],
            ]),
        }
    }

    /// Converts the [CsdV3] into its inner representation.
    pub const fn into_inner(self) -> [u8; Self::LEN] {
        self.0
    }
}

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

impl TryFrom<[u8; CsdV3::LEN]> for CsdV3 {
    type Error = Error;

    fn try_from(val: [u8; Self::LEN]) -> Result<Self> {
        Self::try_from_inner(val)
    }
}

impl TryFrom<&[u8]> for CsdV3 {
    type Error = Error;

    fn try_from(val: &[u8]) -> Result<Self> {
        Self::try_from_bytes(val)
    }
}

impl From<CsdV3> for [u8; CsdV3::LEN] {
    fn from(val: CsdV3) -> Self {
        val.into_inner()
    }
}