sdmmc-core 0.5.0

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

const DEFAULT_CCC_RBL: u16 = 0x5b50;

lib_bitfield! {
    /// Represents the SD memory card command class (`CCC`) + max read data block length (`READ_BL_LEN`).
    ///
    /// **NOTE**: these fields are grouped together for data layout reasons.
    pub CommandClassReadBlockLen(u16): u8 {
        /// Represents whether the card supports `Class 11` commands.
        pub class11: 15;
        /// Represents whether the card supports `Class 10` commands.
        pub class10: 14;
        /// Represents whether the card supports `Class 9` commands.
        pub class9: 13;
        /// Represents whether the card supports `Class 8` commands.
        pub class8: 12;
        /// Represents whether the card supports `Class 7` commands.
        pub class7: 11;
        /// Represents whether the card supports `Class 6` commands.
        pub class6: 10;
        /// Represents whether the card supports `Class 5` commands.
        pub class5: 9;
        /// Represents whether the card supports `Class 4` commands.
        pub class4: 8;
        /// Represents whether the card supports `Class 3` commands.
        pub class3: 7;
        /// Represents whether the card supports `Class 2` commands.
        pub class2: 6;
        /// Represents whether the card supports `Class 1` commands.
        pub class1: 5;
        /// Represents whether the card supports `Class 0` commands.
        pub class0: 4;
        /// Maximum read data block length.
        pub read_data_block_len: 3, 0;
    }
}

impl CommandClassReadBlockLen {
    /// Represents the byte length of the [CommandClassReadBlockLen].
    pub const LEN: usize = 2;

    /// Creates a new [CommandClassReadBlockLen].
    pub const fn new() -> Self {
        Self(DEFAULT_CCC_RBL)
    }

    /// Gets the bit value of the [CommandClassReadBlockLen].
    pub const fn bits(&self) -> u16 {
        self.0
    }

    /// Converts a [`u16`] into a [CommandClassReadBlockLen].
    pub const fn from_bits(val: u16) -> Self {
        Self(val)
    }

    /// Converts the [CommandClassReadBlockLen] into a byte array.
    pub const fn bytes(&self) -> [u8; Self::LEN] {
        self.0.to_be_bytes()
    }

    /// Attempts to convert a byte slice into a [CommandClassReadBlockLen].
    pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
        match val.len() {
            len if len < Self::LEN => Err(Error::InvalidVariant(len)),
            _ => Ok(Self(u16::from_be_bytes([val[0], val[1]]))),
        }
    }
}

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

impl From<u16> for CommandClassReadBlockLen {
    fn from(val: u16) -> Self {
        Self::from_bits(val)
    }
}

impl From<CommandClassReadBlockLen> for u16 {
    fn from(val: CommandClassReadBlockLen) -> Self {
        val.bits()
    }
}

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

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

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

impl<const N: usize> TryFrom<[u8; N]> for CommandClassReadBlockLen {
    type Error = Error;

    fn try_from(val: [u8; N]) -> Result<Self> {
        Self::try_from_bytes(val.as_ref())
    }
}

impl<const N: usize> TryFrom<&[u8; N]> for CommandClassReadBlockLen {
    type Error = Error;

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

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

    #[test]
    fn test_valid() {
        let mut ccc_rbl = CommandClassReadBlockLen::new();
        assert_eq!(ccc_rbl.bits(), DEFAULT_CCC_RBL);
        assert_eq!(ccc_rbl.bytes(), DEFAULT_CCC_RBL.to_be_bytes());

        test_field!(ccc_rbl, class0: 4);
        test_field!(ccc_rbl, class1: 5);
        test_field!(ccc_rbl, class2: 6);
        test_field!(ccc_rbl, class3: 7);
        test_field!(ccc_rbl, class4: 8);
        test_field!(ccc_rbl, class5: 9);
        test_field!(ccc_rbl, class6: 10);
        test_field!(ccc_rbl, class7: 11);
        test_field!(ccc_rbl, class8: 12);
        test_field!(ccc_rbl, class9: 13);
        test_field!(ccc_rbl, class10: 14);
        test_field!(ccc_rbl, class11: 15);

        (0..=u16::MAX).for_each(|raw| {
            let raw_bytes = raw.to_be_bytes();
            let exp_ccc_rbl = CommandClassReadBlockLen(raw);
            let exp_rbl = raw & 0xf;

            assert_eq!(CommandClassReadBlockLen::from_bits(raw), exp_ccc_rbl);
            assert_eq!(
                CommandClassReadBlockLen::try_from_bytes(raw_bytes.as_ref()),
                Ok(exp_ccc_rbl)
            );
            assert_eq!(
                CommandClassReadBlockLen::try_from(raw_bytes.as_ref()),
                Ok(exp_ccc_rbl)
            );
            assert_eq!(
                CommandClassReadBlockLen::try_from(raw_bytes),
                Ok(exp_ccc_rbl)
            );
            assert_eq!(
                CommandClassReadBlockLen::try_from(&raw_bytes),
                Ok(exp_ccc_rbl)
            );

            assert_eq!(exp_ccc_rbl.bits(), raw);
            assert_eq!(exp_ccc_rbl.bytes(), raw_bytes);

            assert_eq!(exp_ccc_rbl.read_data_block_len(), exp_rbl);

            (4..=15).for_each(|class| {
                let is_supported = match class {
                    4 => exp_ccc_rbl.class0(),
                    5 => exp_ccc_rbl.class1(),
                    6 => exp_ccc_rbl.class2(),
                    7 => exp_ccc_rbl.class3(),
                    8 => exp_ccc_rbl.class4(),
                    9 => exp_ccc_rbl.class5(),
                    10 => exp_ccc_rbl.class6(),
                    11 => exp_ccc_rbl.class7(),
                    12 => exp_ccc_rbl.class8(),
                    13 => exp_ccc_rbl.class9(),
                    14 => exp_ccc_rbl.class10(),
                    _ => exp_ccc_rbl.class11(),
                };

                assert_eq!(
                    is_supported,
                    (raw & (1 << class)) != 0,
                    "class: {}",
                    class - 4
                );
            });
        });
    }

    #[test]
    fn test_invalid() {
        let raw = CommandClassReadBlockLen::new().bytes();

        (0..CommandClassReadBlockLen::LEN).for_each(|len| {
            let exp_err = Error::InvalidVariant(len);
            assert_eq!(
                CommandClassReadBlockLen::try_from_bytes(raw[..len].as_ref()),
                Err(exp_err)
            );
            assert_eq!(
                CommandClassReadBlockLen::try_from(raw[..len].as_ref()),
                Err(exp_err)
            );
        });
    }
}