sdmmc-core 0.5.0

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

mod rate_unit;
mod time_value;

pub use rate_unit::*;
pub use time_value::*;

const UNIT_MASK: u8 = 0x3;
const VALUE_MASK: u8 = 0x78;
const VALUE_SHIFT: usize = 3;
const SPEED_MASK: u8 = 0x7f;
const DEFAULT_SPEED: u8 = 0x32;

/// Represents the maximum data transfer speed (`TRAN_SPEED`) for the SD memory card.
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TransferSpeed(u8);

impl TransferSpeed {
    /// Creates a new [TransferSpeed].
    pub const fn new() -> Self {
        Self(DEFAULT_SPEED)
    }

    /// Gets the bit value for the [TransferSpeed].
    pub const fn bits(&self) -> u8 {
        self.0
    }

    /// Creates a new [TransferSpeed] from inner parts.
    pub const fn from_parts(rate_unit: TransferRateUnit, time_value: TransferTimeValue) -> Self {
        Self((time_value.into_raw() << VALUE_SHIFT) | rate_unit.into_raw())
    }

    /// Attempts to convert a [`u8`] into a [TransferSpeed].
    pub const fn try_from_bits(val: u8) -> Result<Self> {
        match (
            TransferRateUnit::from_raw(val & UNIT_MASK),
            TransferTimeValue::from_raw((val & VALUE_MASK) >> VALUE_SHIFT),
        ) {
            (Ok(_), Ok(_)) => Ok(Self(val & SPEED_MASK)),
            (Err(err), _) => Err(err),
            (_, Err(err)) => Err(err),
        }
    }

    /// Gets the [TransferRateUnit] for the [TransferSpeed].
    pub const fn rate_unit(&self) -> TransferRateUnit {
        match self.try_rate_unit() {
            Ok(val) => val,
            Err(_) => TransferRateUnit::new(),
        }
    }

    /// Attempts to get the [TransferRateUnit] for the [TransferSpeed].
    pub const fn try_rate_unit(&self) -> Result<TransferRateUnit> {
        TransferRateUnit::from_raw(self.0 & UNIT_MASK)
    }

    /// Gets the [TransferTimeValue] for the [TransferSpeed].
    pub const fn time_value(&self) -> TransferTimeValue {
        match self.try_time_value() {
            Ok(val) => val,
            Err(_) => TransferTimeValue::new(),
        }
    }

    /// Attempts to get the [TransferTimeValue] for the [TransferSpeed].
    pub const fn try_time_value(&self) -> Result<TransferTimeValue> {
        TransferTimeValue::from_raw((self.0 & VALUE_MASK) >> VALUE_SHIFT)
    }

    /// Gets the total transfer rate (in kbit/s) for the [TransferSpeed].
    pub const fn rate_kbits(&self) -> u32 {
        (self.time_value().value_int() * self.rate_unit().kbits()) / 10
    }
}

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

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

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

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

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

    #[test]
    fn test_valid() {
        let taac = TransferSpeed::new();

        assert_eq!(taac.try_rate_unit(), Ok(TransferRateUnit::new()));
        assert_eq!(taac.rate_unit(), TransferRateUnit::new());

        assert_eq!(taac.try_time_value(), Ok(TransferTimeValue::new()));
        assert_eq!(taac.time_value(), TransferTimeValue::new());

        (0..=u8::MAX)
            .filter(|b| {
                TransferRateUnit::from_raw(b & UNIT_MASK).is_ok()
                    && TransferTimeValue::from_raw((b & VALUE_MASK) >> VALUE_SHIFT).is_ok()
            })
            .for_each(|raw| {
                let exp_taac = TransferSpeed(raw & SPEED_MASK);
                let exp_unit = TransferRateUnit::from_raw(raw & UNIT_MASK).unwrap();
                let exp_value =
                    TransferTimeValue::from_raw((raw & VALUE_MASK) >> VALUE_SHIFT).unwrap();
                let exp_kbits = (exp_value.value_int() * exp_unit.kbits()) / 10;

                assert_eq!(TransferSpeed::try_from_bits(raw), Ok(exp_taac));
                assert_eq!(TransferSpeed::try_from(raw), Ok(exp_taac));

                assert_eq!(exp_taac.bits(), raw & SPEED_MASK);
                assert_eq!(u8::from(exp_taac), raw & SPEED_MASK);

                assert_eq!(exp_taac.rate_unit(), exp_unit);
                assert_eq!(exp_taac.time_value(), exp_value);
                assert_eq!(exp_taac.rate_kbits(), exp_kbits);
            });
    }

    #[test]
    fn test_invalid() {
        (0..=u8::MAX)
            .filter(|b| TransferRateUnit::from_raw(b & UNIT_MASK).is_err())
            .for_each(|raw| {
                let exp_err = Error::InvalidVariant((raw & UNIT_MASK) as usize);

                assert_eq!(TransferSpeed::try_from_bits(raw), Err(exp_err));
                assert_eq!(TransferSpeed::try_from(raw), Err(exp_err));
            });

        (0..=u8::MAX)
            .filter(|b| {
                TransferRateUnit::from_raw(b & UNIT_MASK).is_ok()
                    && TransferTimeValue::from_raw((b & VALUE_MASK) >> VALUE_SHIFT).is_err()
            })
            .for_each(|raw| {
                let exp_err = Error::InvalidVariant(((raw & VALUE_MASK) >> VALUE_SHIFT) as usize);

                assert_eq!(TransferSpeed::try_from_bits(raw), Err(exp_err));
                assert_eq!(TransferSpeed::try_from(raw), Err(exp_err));
            });
    }
}