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

pub use flags::*;

const COMMAND_INDEX: u8 = 0b11_0100;

lib_bitfield! {
    /// Represents the response to the `IO_RW_DIRECT` command (`CMD52`).
    pub R5(MSB0 [u8; 6]): u8 {
        start: 47;
        direction: 46;
        raw_command_index: 45, 40;
        raw_flags: 23, 16;
        /// Read or write data for the [R5] response.
        pub data: 15, 8;
        raw_crc: 7, 1;
        end: 0;
    }
}

response! {
    R5 {
        response_mode: Sd,
    }
}

impl R5 {
    /// Represents the byte length of [R5].
    pub const LEN: usize = 6;
    /// Represents the default byte value of [R5].
    pub const DEFAULT: [u8; Self::LEN] = [0x34, 0, 0, 0, 0, 0x45];

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

    /// Gets the `Command Index` field of the [R5].
    pub const fn command_index(&self) -> u8 {
        self.raw_command_index()
    }

    /// Gets the [Flags] field of the [R5].
    pub const fn flags(&self) -> Result<Flags> {
        Flags::try_from_bits(self.raw_flags())
    }

    /// Sets the [Flags] field of the [R5].
    pub fn set_flags(&mut self, val: Flags) {
        self.set_raw_flags(val.bits());
    }

    /// Attempts to convert a byte slice into a [R5].
    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 raw_crc = val[5] >> 1;
                let exp_crc = Crc7::calculate(&[val[0], val[1], val[2], val[3], val[4]]).bits();

                match Self([val[0], val[1], val[2], val[3], val[4], val[5]]) {
                    r5 if r5.start() => Err(Error::invalid_field_variant("r5::start", 1)),
                    r5 if r5.direction() => Err(Error::invalid_field_variant("r5::direction", 1)),
                    r5 if r5.command_index() != COMMAND_INDEX => Err(Error::invalid_field_variant(
                        "r5::command_index",
                        r5.command_index() as usize,
                    )),
                    r5 if r5.flags().is_err() => Err(Error::invalid_field_variant(
                        "r5::flags",
                        r5.raw_flags() as usize,
                    )),
                    r5 if raw_crc != exp_crc => Err(Error::invalid_crc7(raw_crc, exp_crc)),
                    r5 if !r5.end() => Err(Error::invalid_field_variant("r5::end", 0)),
                    r5 => Ok(r5),
                }
            }
        }
    }
}

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

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

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

impl<const N: usize> TryFrom<[u8; N]> for R5 {
    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 R5 {
    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 r5 = R5::new();

        let mut flags = r5.flags().unwrap();

        assert!(!flags.out_of_range());
        assert!(!flags.function_number());
        assert!(!flags.error());
        assert!(!flags.illegal_command());
        assert!(!flags.crc_error());

        test_field!(flags, out_of_range: 0);
        test_field!(flags, function_number: 1);
        test_field!(flags, error: 3);
        test_field!(flags, illegal_command: 6);
        test_field!(flags, crc_error: 7);

        assert_eq!(flags.io_current_state(), Ok(IoCurrentState::new()));

        assert_eq!(r5.data(), 0);

        (0..=u8::MAX)
            .zip(0..=u8::MAX)
            .for_each(|(raw_flags, data)| {
                let (raw, crc) = raw_with_crc([COMMAND_INDEX, 0, 0, raw_flags, data, 0]);

                match Flags::try_from_bits(raw_flags) {
                    Ok(flags) => {
                        r5 = R5(raw);

                        assert_eq!(R5::try_from_bytes(raw.as_ref()), Ok(r5));
                        assert_eq!(R5::try_from(raw), Ok(r5));

                        assert_eq!(r5.flags(), Ok(flags));
                        assert_eq!(r5.data(), data);

                        r5.set_flags(Flags::new());
                        assert_eq!(r5.flags(), Ok(Flags::new()));

                        r5.set_flags(flags);
                        assert_eq!(r5.flags(), Ok(flags));

                        r5.set_data(0);
                        assert_eq!(r5.data(), 0);

                        r5.set_data(data);
                        assert_eq!(r5.data(), data);
                    }
                    Err(_) => {
                        assert_eq!(
                            R5::try_from_bytes(raw.as_ref()),
                            Err(Error::invalid_field_variant(
                                "r5::flags",
                                raw_flags as usize
                            ))
                        );
                        assert_eq!(
                            R5::try_from(raw),
                            Err(Error::invalid_field_variant(
                                "r5::flags",
                                raw_flags as usize
                            ))
                        );
                    }
                }
            });
    }
}