sdmmc-core 0.5.0

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

mod voltage_accepted;

pub use voltage_accepted::*;

const COMMAND_INDEX: u8 = 0b00_1000;

lib_bitfield! {
    /// Represents the response to the `SEND_IF_COND` command (`CMD8`) in SD mode.
    pub R7(MSB0 [u8; 6]): u8 {
        start_bit: 47;
        direction: 46;
        raw_command_index: 45, 40;
        /// PCIe VDD3 (1.2V power rail) supported.
        pub pcie_12v_support: 21;
        /// PCIe accepted by card.
        pub pcie: 20;
        raw_voltage_accepted: 19, 16;
        /// Echo-back of the check pattern for the [R7].
        pub echo_check: 15, 8;
        raw_crc: 7, 1;
        end_bit: 0;
    }
}

response! {
    R7 {
        response_mode: Sd,
    }
}

impl R7 {
    /// Represents the byte length of the [R7] response in SD mode.
    pub const LEN: usize = 6;
    /// Represents the default byte value of [R7].
    pub const DEFAULT: [u8; Self::LEN] = [COMMAND_INDEX, 0, 0, 0x1, 0, 0x41];

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

    /// Gets the command index field for the [R7].
    pub const fn command_index(&self) -> u8 {
        self.raw_command_index()
    }

    /// Gets the [VoltageAccepted] value of the [R7] response.
    pub const fn voltage_accepted(&self) -> Result<VoltageAccepted> {
        VoltageAccepted::from_raw(self.raw_voltage_accepted())
    }

    /// Sets the [VoltageAccepted] value of the [R7] response.
    pub fn set_voltage_accepted(&mut self, voltage_accepted: VoltageAccepted) {
        self.set_raw_voltage_accepted(voltage_accepted.into_raw());
    }

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

    /// Calculates and sets the [Crc7] field of the [R7].
    ///
    /// Returns the calculated [Crc7].
    pub fn calculate_crc(&mut self) -> Crc7 {
        let crc = Crc7::calculate(self.0[..Self::LEN - 1].as_ref());
        self.set_raw_crc(crc.bits());
        crc
    }

    /// Attempts to convert a byte slice into a [R7].
    pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
        match val.len() {
            len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
            _ => {
                let crc = Crc7::calculate(&[val[0], val[1], val[2], val[3], val[4]]);

                match R7([val[0], val[1], val[2], val[3], val[4], val[5]]) {
                    r7 if r7.start_bit() => Err(Error::invalid_field_variant("r7::start_bit", 1)),
                    r7 if r7.direction() => Err(Error::invalid_field_variant("r7::direction", 1)),
                    r7 if r7.command_index() != COMMAND_INDEX => Err(Error::invalid_field_variant(
                        "r7::command_index",
                        r7.command_index() as usize,
                    )),
                    r7 if r7.voltage_accepted().is_err() => Err(Error::invalid_field_variant(
                        "r7::voltage_accepted",
                        r7.raw_voltage_accepted() as usize,
                    )),
                    r7 if r7.raw_crc() != crc.bits() => {
                        Err(Error::invalid_crc7(r7.raw_crc(), crc.bits()))
                    }
                    r7 if !r7.end_bit() => Err(Error::invalid_field_variant("r7::end_bit", 0)),
                    r7 => Ok(r7),
                }
            }
        }
    }
}

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

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

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

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

impl<const N: usize> TryFrom<[u8; N]> for R7 {
    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 R7 {
    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;
    use crate::util::raw_with_crc;

    #[test]
    fn test_fields() {
        let mut r7 = R7::new();

        assert!(!r7.pcie());
        assert!(!r7.pcie_12v_support());

        test_field!(r7, pcie { bit: 20, [u8; R7::LEN] });
        test_field!(r7, pcie_12v_support { bit: 21, [u8; R7::LEN] });

        assert_eq!(r7.voltage_accepted(), Ok(VoltageAccepted::new()));
        assert_eq!(r7.echo_check(), 0);

        (0..=u8::MAX).zip(0..=u8::MAX).for_each(|(flags, echo)| {
            let (raw, _crc) = raw_with_crc([COMMAND_INDEX, 0, 0, flags, echo, 0]);
            let raw_va = flags & 0xf;

            match VoltageAccepted::from_raw(raw_va) {
                Ok(va) => {
                    r7 = R7(raw);

                    assert_eq!(R7::try_from_bytes(raw.as_ref()), Ok(r7));
                    assert_eq!(R7::try_from(raw), Ok(r7));
                    assert_eq!(r7.bytes(), raw);

                    test_field!(r7, pcie { bit: 20, [u8; R7::LEN] });
                    test_field!(r7, pcie_12v_support { bit: 21, [u8; R7::LEN] });

                    assert_eq!(r7.voltage_accepted(), Ok(va));

                    r7.set_voltage_accepted(VoltageAccepted::new());
                    assert_eq!(r7.voltage_accepted(), Ok(VoltageAccepted::new()));

                    r7.set_voltage_accepted(va);
                    assert_eq!(r7.voltage_accepted(), Ok(va));

                    assert_eq!(r7.echo_check(), echo);

                    r7.set_echo_check(0);
                    assert_eq!(r7.echo_check(), 0);

                    r7.set_echo_check(echo);
                    assert_eq!(r7.echo_check(), echo);
                }
                Err(_) => {
                    assert_eq!(
                        R7::try_from_bytes(raw.as_ref()),
                        Err(Error::invalid_field_variant(
                            "r7::voltage_accepted",
                            raw_va as usize
                        ))
                    );
                    assert_eq!(
                        R7::try_from(raw),
                        Err(Error::invalid_field_variant(
                            "r7::voltage_accepted",
                            raw_va as usize
                        ))
                    );
                }
            }
        });
    }
}