sdmmc-core 0.5.0

SD/MMC core data structures and algorithms
Documentation
/// Convenience macro to define a `Command` type.
#[macro_export]
macro_rules! command {
    (
        $(#[$docs:meta])+
        $cmd:ident {
            command_type: $cmd_type:expr,
            command_class: $cmd_class:expr,
            command_index: $cmd_idx:expr,
            argument: $arg:ident,
            default_arg: $default_arg:expr,
            response_type: $res_type:expr,
        }
    ) => {
        paste::paste! {
            $crate::lib_bitfield! {
                $(#[$docs])+
                pub $cmd(MSB0 [u8; $crate::command::CMD_LEN]): u32 {
                    start_bit: 47;
                    direction: 46;
                    raw_command_index: 45, 40;
                    raw_argument: 39, 8;
                    raw_crc: 7, 1;
                    end_bit: 0;
                }
            }

            impl $cmd {
                #[doc = "Represents the byte length of the [" $cmd "]."]
                pub const LEN: usize = $crate::command::CMD_LEN;
                /// Represents the command index of the [$cmd].
                pub const COMMAND_INDEX: u8 = $cmd_idx;

                /// Creates a new [$cmd].
                pub const fn new() -> Self {
                    Self(Self::default_bytes())
                }

                #[doc = "Gets the start sequence of the [" $cmd "]."]
                #[inline]
                pub const fn start() -> u8 {
                    0x40 | Self::COMMAND_INDEX
                }

                #[inline]
                const fn default_bytes() -> [u8; Self::LEN] {
                    let [a0, a1, a2, a3] = $default_arg;
                    let raw = [Self::start(), a0, a1, a2, a3];
                    let crc = $crate::crc::Crc7::calculate(&raw);
                    let [r0, r1, r2, r3, r4] = raw;

                    [r0, r1, r2, r3, r4, (crc.bits() << 1) | 1]
                }

                #[doc = "Gets the command type for the [" $cmd "]."]
                #[inline]
                pub const fn command_type(&self) -> $crate::command::CommandType {
                    $cmd_type
                }

                #[doc = "Gets the command class for the [" $cmd "]."]
                #[inline]
                pub const fn command_class(&self) -> $crate::command::CommandClass {
                    $cmd_class
                }

                #[doc = "Gets the command indnex field of the [" $cmd "]."]
                pub const fn command_index(&self) -> u8 {
                    self.raw_command_index() as u8
                }

                #[doc = "Gets the raw `argument` field of the [" $cmd "]."]
                pub const fn argument_bits(&self) -> u32 {
                    self.raw_argument()
                }

                #[doc = "Gets the `argument` field of the [" $cmd "]."]
                pub const fn argument(&self) -> $crate::result::Result<$arg> {
                    $arg::try_from_bits(self.raw_argument())
                }

                #[doc = "Sets the `argument` field of the [" $cmd "]."]
                pub fn set_argument(&mut self, val: $arg) {
                    self.set_raw_argument(val.bits())
                }

                #[doc = "Gets the `CRC7` field of the [" $cmd "]."]
                pub const fn crc(&self) -> $crate::crc::Crc7 {
                    $crate::crc::Crc7::from_bits(self.raw_crc() as u8)
                }

                #[doc = "Calculates and sets the `CRC7` field of the [" $cmd "]."]
                pub fn calculate_crc(&mut self) -> $crate::crc::Crc7 {
                    let crc = $crate::crc::Crc7::calculate(self.0[..Self::LEN - 1].as_ref());
                    self.set_raw_crc(crc.bits() as u32);
                    crc
                }

                #[doc = "Gets the expected response type for the [" $cmd "]."]
                #[inline]
                pub const fn response_type(&self) -> $crate::response::ResponseType {
                    $res_type
                }

                #[doc = "Attempts to convert a byte slice into a [" $cmd "]."]
                pub const fn try_from_bytes(val: &[u8]) -> $crate::result::Result<Self> {
                    use $crate::result::Error;

                    match val.len() {
                        len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
                        _ => {
                            let crc = $crate::crc::Crc7::calculate(&[val[0], val[1], val[2], val[3], val[4]]);

                            match Self([val[0], val[1], val[2], val[3], val[4], val[5]]) {
                                cmd if cmd.start_bit() => Err(Error::invalid_field_variant("cmd::start_bit", 1)),
                                cmd if !cmd.direction() => Err(Error::invalid_field_variant("cmd::direction", 0)),
                                cmd if cmd.command_index() != Self::COMMAND_INDEX => Err(Error::invalid_field_variant(
                                    "cmd::command_index",
                                    cmd.command_index() as usize,
                                )),
                                cmd if cmd.argument().is_err() => Err(Error::invalid_field_variant(
                                    "cmd::argument",
                                    cmd.raw_argument() as usize,
                                )),
                                cmd if cmd.raw_crc() as u8 != crc.bits() => {
                                    Err(Error::invalid_crc7(cmd.raw_crc() as u8, crc.bits()))
                                }
                                cmd if !cmd.end_bit() => Err(Error::invalid_field_variant("cmd::end_bit", 0)),
                                cmd => Ok(cmd),
                            }
                        }
                    }
                }
            }

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

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

            impl TryFrom<&[u8]> for $cmd {
                type Error = $crate::result::Error;

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

            impl<const N: usize> TryFrom<&[u8; N]> for $cmd {
                type Error = $crate::result::Error;

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

            impl<const N: usize> TryFrom<[u8; N]> for $cmd {
                type Error = $crate::result::Error;

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

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

                #[test]
                fn test_fields() {
                    let new_arg = $arg::new();

                    (1..=u32::BITS).map(|r| ((1u64 << r) - 1) as u32).for_each(|raw_arg| {
                        let start = $cmd::start();
                        let [arg0, arg1, arg2, arg3] = raw_arg.to_be_bytes();
                        let (raw, crc) = raw_with_crc([start, arg0, arg1, arg2, arg3, 0]);

                        match $arg::try_from_bits(raw_arg) {
                            Ok(exp_arg) => {

                                let mut exp_cmd = $cmd(raw);

                                assert_eq!($cmd::try_from_bytes(&raw), Ok(exp_cmd));
                                assert_eq!($cmd::try_from(&raw), Ok(exp_cmd));

                                assert_eq!(exp_cmd.bytes(), raw);
                                assert_eq!(exp_cmd.command_index(), $cmd::COMMAND_INDEX);
                                assert_eq!(exp_cmd.argument(), Ok(exp_arg));
                                assert_eq!(exp_cmd.crc(), crc);

                                exp_cmd.set_argument(new_arg);
                                assert_eq!(exp_cmd.argument(), Ok(new_arg));

                                exp_cmd.set_argument(exp_arg);
                                assert_eq!(exp_cmd.argument(), Ok(exp_arg));
                            }
                            Err(_err) => {
                                let exp_err = Err($crate::result::Error::invalid_field_variant("cmd::argument", raw_arg as usize));

                                assert_eq!($cmd::try_from_bytes(&raw), exp_err);
                                assert_eq!($cmd::try_from(&raw), exp_err);
                            }
                        }
                    });
                }
            }
        }
    };
}

/// Convenience macro to define a wrapper enum around command class variants.
#[macro_export]
macro_rules! command_enum {
    (
        $(#[$doc:meta])*
        $cmd:ident {
            default: $default_cmd:ident ($default_cmd_ty:expr),
            $(
                $(#[$doc_var:meta])*
                $cmd_var:ident ($cmd_mod:ident :: $cmd_inner:ident),
            )+
        }
    ) => {
        $(#[$doc])*
        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
        pub enum $cmd {
            $(
                $(#[$doc_var])*
                $cmd_var($cmd_mod::$cmd_inner),
            )+
        }

        ::paste::paste! {
            impl $cmd {
                #[doc = "Represents the byte length of the [" $cmd "]."]
                pub const LEN: usize = 6;

                #[doc = "Creates a new [" $cmd "]."]
                pub const fn new() -> Self {
                    Self::$default_cmd($default_cmd_ty::new())
                }

                #[doc = "Gets the command index for the [" $cmd "]."]
                pub const fn command_index(&self) -> u8 {
                    match self {
                        $(
                            Self::$cmd_var(cmd) => cmd.command_index(),
                        )+
                    }
                }

                #[doc = "Gets the command type for the [" $cmd "]."]
                pub const fn command_type(&self) -> $crate::command::CommandType {
                    match self {
                        $(
                            Self::$cmd_var(cmd) => cmd.command_type(),
                        )+
                    }
                }

                #[doc = "Gets the response type for the [" $cmd "]."]
                pub const fn response_type(&self) -> $crate::response::ResponseType {
                    match self {
                        $(
                            Self::$cmd_var(cmd) => cmd.response_type(),
                        )+
                    }
                }

                #[doc = "Gets the raw command argument value for the [" $cmd "]."]
                pub fn argument(&self) -> $crate::result::Result<u32> {
                    match self {
                        $(
                            Self::$cmd_var(cmd) => cmd.argument().map(|arg| arg.bits()),
                        )+
                    }
                }

                #[doc = "Gets the `CRC7` field of the [" $cmd "]."]
                pub const fn crc(&self) -> $crate::crc::Crc7 {
                    match self {
                        $(
                            Self::$cmd_var(cmd) => cmd.crc(),
                        )+
                    }
                }

                #[doc = "Converts the [" $cmd "] into a byte array."]
                pub const fn bytes(&self) -> [u8; Self::LEN] {
                    match self {
                        $(
                            Self::$cmd_var(cmd) => cmd.bytes(),
                        )+
                    }
                }
            }

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

            $(
                impl From<$cmd_mod::$cmd_inner> for $cmd {
                    fn from(val: $cmd_mod::$cmd_inner) -> Self {
                        Self::$cmd_var(val)
                    }
                }

                impl TryFrom<$cmd> for $cmd_mod::$cmd_inner {
                    type Error = $crate::result::Error;

                    fn try_from(val: $cmd) -> $crate::result::Result<Self> {
                        match val {
                            $cmd::$cmd_var(cmd) => Ok(cmd),
                            _ => Err(Self::Error::invalid_field_variant("command", val.command_index() as usize)),
                        }
                    }
                }
            )+
        }
    };
}