sdmmc-core 0.5.0

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

lib_bitfield! {
    /// Represents the starting byte of card responses in SD mode.
    pub Start(u8): u8 {
        /// The MSB of the response, should always be `0`.
        pub start_bit: 7;
        /// The transmission bit, should always be `0`.
        pub transmission_bit: 6;
        /// Represents a 6-bit index ranging from `0-63`, indicating the `CMD` number for the response.
        pub command_index: 5, 0;
    }
}

impl Start {
    /// Represents the byte length of the [Start].
    pub const LEN: usize = 1;
    /// Represents the default value of the [Start].
    pub const DEFAULT: u8 = 0;
    /// Represents the valid bitmask of the [Start].
    pub const MASK: u8 = 0b0011_1111;

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

    /// Converts a [`u8`] into a [Start].
    ///
    /// # Note
    ///
    /// Masks the value to a valid [Start] value.
    pub const fn from_bits(val: u8) -> Self {
        Self(val & Self::MASK)
    }

    /// Attempts to convert a [`u8`] into a [Start].
    pub const fn try_from_bits(val: u8) -> Result<Self> {
        match val {
            v if (v & !Self::MASK) != 0 => Err(Error::invalid_field_variant("start", v as usize)),
            v => Ok(Self(v)),
        }
    }
}

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

impl TryFrom<u8> for Start {
    type Error = Error;

    fn try_from(val: u8) -> Result<Self> {
        Self::try_from_bits(val)
    }
}

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

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

    #[test]
    fn test_fields() {
        let mut s = Start::new();

        assert_eq!(s.command_index(), 0);
        assert!(!s.transmission_bit());
        assert!(!s.start_bit());

        (0..=0x3f).for_each(|cmd| {
            s.set_command_index(cmd);
            assert_eq!(s.command_index(), cmd);

            // make sure we didn't corrupt other fields
            assert!(!s.transmission_bit());
            assert!(!s.start_bit());
        });

        // the interface will accept invalid command index, but masks the value.
        (0x40..=u8::MAX).for_each(|invalid_cmd| {
            s.set_command_index(invalid_cmd);
            assert_eq!(s.command_index(), invalid_cmd & Start::MASK);
        });
    }
}