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 card_status;

pub use card_status::*;

/// Represents the command index field value of the [R6] response in SD mode.
pub const COMMAND_INDEX: u8 = 0b11;

lib_bitfield! {
    /// Represents the Published RCA response in SD mode.
    pub R6(MSB0 [u8; 6]): u16 {
        start_bit: 47;
        direction: 46;
        raw_command_index: 45, 40;
        /// The new published RCA number of the card.
        pub rca: 39, 24;
        raw_card_status: 23, 8;
        raw_crc: 7, 1;
        end_bit: 0;
    }
}

response! {
    R6 {
        response_mode: Sd,
    }
}

impl R6 {
    /// Represents the byte length of the [R6] response in SD mode.
    pub const LEN: usize = 6;
    pub const DEFAULT: [u8; Self::LEN] = [COMMAND_INDEX, 0x00, 0x00, 0x00, 0x00, 0x45];

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

    /// Gets the command index field  of the [R6].
    ///
    /// Should always be [COMMAND_INDEX].
    pub const fn command_index(&self) -> u8 {
        self.raw_command_index() as u8
    }

    /// Gets the [CardStatus] for the [R6].
    pub const fn card_status(&self) -> Result<CardStatus> {
        CardStatus::try_from_bits(self.raw_card_status())
    }

    /// Sets the [CardStatus] for the [R6].
    pub fn set_card_status(&mut self, val: CardStatus) {
        self.set_raw_card_status(val.bits());
    }

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

    /// Calculates and sets the [Crc7] field of the [R6].
    ///
    /// 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() as u16);
        crc
    }

    /// Attempts to convert a byte slice into a [R6].
    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 R6([val[0], val[1], val[2], val[3], val[4], val[5]]) {
                    r6 if r6.start_bit() => Err(Error::invalid_field_variant("r6::start_bit", 1)),
                    r6 if r6.direction() => Err(Error::invalid_field_variant("r6::direction", 1)),
                    r6 if r6.command_index() != COMMAND_INDEX => Err(Error::invalid_field_variant(
                        "r6::command_index",
                        r6.command_index() as usize,
                    )),
                    r6 if r6.card_status().is_err() => Err(Error::invalid_field_variant(
                        "r6::card_status",
                        r6.raw_card_status() as usize,
                    )),
                    r6 if r6.raw_crc() as u8 != crc.bits() => {
                        Err(Error::invalid_crc7(r6.raw_crc() as u8, crc.bits()))
                    }
                    r6 if !r6.end_bit() => Err(Error::invalid_field_variant("r6::end_bit", 0)),
                    r6 => Ok(r6),
                }
            }
        }
    }
}

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

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

    #[test]
    fn test_fields() {
        let mut r6 = R6::new();

        assert_eq!(r6.card_status(), Ok(CardStatus::new()));

        let raw_cs = [0, 1, 2, 3, 4, 5, 6, 7, 8];

        raw_cs
            .into_iter()
            .zip([
                CurrentState::Idle,
                CurrentState::Ready,
                CurrentState::Identification,
                CurrentState::Standby,
                CurrentState::Transfer,
                CurrentState::Data,
                CurrentState::Receive,
                CurrentState::Program,
                CurrentState::Disabled,
            ])
            .for_each(|(cstate_raw, cstate)| {
                assert_eq!(CurrentState::from_raw(cstate_raw), Ok(cstate));
                let cs = CardStatus::try_from_bits((cstate_raw as u16) << 9).unwrap();

                r6.set_card_status(cs);
                assert_eq!(r6.card_status(), Ok(cs));

                let (raw, _crc) = raw_with_crc([COMMAND_INDEX, 0, 0, cstate_raw << 1, 0, 0]);

                let exp_r6 = R6(raw);
                assert_eq!(R6::try_from_bytes(raw.as_ref()), Ok(exp_r6));

                assert_eq!(exp_r6.card_status(), Ok(cs));
            });

        (0..=0xf)
            .filter(|r| !raw_cs.iter().any(|cs| cs == r))
            .for_each(|invalid_cs| {
                let err_cs = u16::from_be_bytes([invalid_cs << 1, 0]);
                assert_eq!(
                    R6::try_from_bytes(&[COMMAND_INDEX, 0, 0, invalid_cs << 1, 0, 0]),
                    Err(Error::invalid_field_variant(
                        "r6::card_status",
                        err_cs as usize
                    ))
                );
            });

        (1..=u16::BITS)
            .map(|r| ((1u32 << r) - 1) as u16)
            .for_each(|rca| {
                let [rca0, rca1] = rca.to_be_bytes();
                let (raw, _crc) = raw_with_crc([0x03, rca0, rca1, 0, 0, 0]);
                let mut exp_r6 = R6(raw);

                assert_eq!(R6::try_from_bytes(raw.as_ref()), Ok(exp_r6));
                assert_eq!(exp_r6.rca(), rca);

                exp_r6.set_rca(0);
                assert_eq!(exp_r6.rca(), 0);

                exp_r6.set_rca(rca);
                assert_eq!(exp_r6.rca(), rca);
            });
    }
}