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 address field of the [Argument](super::Argument).
    ///
    /// The field is a 17-bit field split into two sub-fields:
    ///
    /// - `[0:8]`: offset address in the specified page
    /// - `[9:16]`: page number
    pub Address(MSB0 [u8; 3]): u16 {
        raw_page_number: 16, 9;
        raw_offset_address: 8, 0;
    }
}

impl Address {
    /// Represents the byte length of the [Address].
    pub const LEN: usize = 3;
    /// Represents the default value of the [Address].
    pub const DEFAULT: [u8; Self::LEN] = [0; Self::LEN];

    const MASK: u32 = 0x1ffff;

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

    /// Gets the offset address in the page specified by `page_number`.
    pub const fn page_number(&self) -> u8 {
        self.raw_page_number() as u8
    }

    /// Sets the offset address in the page specified by `page_number`.
    pub fn set_page_number(&mut self, val: u8) {
        self.set_raw_page_number(val as u16);
    }

    /// Gets the offset address in the page specified by `offset_address`.
    pub const fn offset_address(&self) -> u16 {
        self.raw_offset_address()
    }

    /// Sets the offset address in the page specified by `offset_address`.
    ///
    /// # Note
    ///
    /// The valid address range is: `0x0 - 0x1ff`.
    pub fn set_offset_address(&mut self, val: u16) {
        self.set_raw_offset_address(val);
    }

    /// Converts the [Address] into a [`u32`].
    pub const fn bits(&self) -> u32 {
        let [r0, r1, r2] = self.0;
        u32::from_be_bytes([0, r0, r1, r2])
    }

    /// Converts a [`u32`] into an [Address].
    pub const fn from_bits(val: u32) -> Self {
        match Self::try_from_bits(val) {
            Ok(a) => a,
            Err(_) => panic!("cmd: argument: addr: invalid bit pattern"),
        }
    }

    /// Attempts to convert a [`u32`] into an [Address].
    pub const fn try_from_bits(val: u32) -> Result<Self> {
        let [_, r0, r1, r2] = (val & Self::MASK).to_be_bytes();
        Ok(Self([r0, r1, r2]))
    }

    /// Attempts to convert a byte slice into an [Argument](super::Argument).
    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)),
            _ => Ok(Self([val[0] & 0x1, val[1], val[2]])),
        }
    }
}

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

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

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

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

impl<const N: usize> TryFrom<&[u8; N]> for Address {
    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 Address {
    type Error = Error;

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

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

    #[test]
    fn test_fields() {
        (1..=17).map(|r| (1u32 << r) - 1).for_each(|raw_arg| {
            let [_, r0, r1, r2] = raw_arg.to_be_bytes();
            let raw = [r0, r1, r2];

            let mut exp_addr = Address(raw);

            let exp_page_num = ((raw_arg & 0x1_fe00) >> 9) as u8;
            let exp_offset_addr = (raw_arg & 0x1ff) as u16;

            assert_eq!(Address::try_from_bytes(&raw), Ok(exp_addr));
            assert_eq!(Address::try_from_bits(raw_arg), Ok(exp_addr));
            assert_eq!(Address::try_from(raw), Ok(exp_addr));
            assert_eq!(Address::try_from(&raw), Ok(exp_addr));

            assert_eq!(exp_addr.bytes(), raw);

            assert_eq!(exp_addr.page_number(), exp_page_num);

            exp_addr.set_page_number(0);
            assert_eq!(exp_addr.page_number(), 0);

            exp_addr.set_page_number(exp_page_num);
            assert_eq!(exp_addr.page_number(), exp_page_num);

            assert_eq!(exp_addr.offset_address(), exp_offset_addr);

            exp_addr.set_offset_address(0);
            assert_eq!(exp_addr.offset_address(), 0);

            exp_addr.set_offset_address(exp_offset_addr);
            assert_eq!(exp_addr.offset_address(), exp_offset_addr);
        });
    }
}