sdmmc_core/macros/
command.rs

1/// Convenience macro to define a `Command` type.
2#[macro_export]
3macro_rules! command {
4    (
5        $(#[$docs:meta])+
6        $cmd:ident {
7            command_type: $cmd_type:expr,
8            command_class: $cmd_class:expr,
9            command_index: $cmd_idx:expr,
10            argument: $arg:ident,
11            default_arg: $default_arg:expr,
12            response_type: $res_type:expr,
13        }
14    ) => {
15        paste::paste! {
16            $crate::lib_bitfield! {
17                $(#[$docs])+
18                pub $cmd(MSB0 [u8; $crate::command::CMD_LEN]): u32 {
19                    start_bit: 47;
20                    direction: 46;
21                    raw_command_index: 45, 40;
22                    raw_argument: 39, 8;
23                    raw_crc: 7, 1;
24                    end_bit: 0;
25                }
26            }
27
28            impl $cmd {
29                #[doc = "Represents the byte length of the [" $cmd "]."]
30                pub const LEN: usize = $crate::command::CMD_LEN;
31                /// Represents the command index of the [$cmd].
32                pub const COMMAND_INDEX: u8 = $cmd_idx;
33
34                /// Creates a new [$cmd].
35                pub const fn new() -> Self {
36                    Self(Self::default_bytes())
37                }
38
39                #[doc = "Gets the start sequence of the [" $cmd "]."]
40                #[inline]
41                pub const fn start() -> u8 {
42                    0x40 | Self::COMMAND_INDEX
43                }
44
45                #[inline]
46                const fn default_bytes() -> [u8; Self::LEN] {
47                    let [a0, a1, a2, a3] = $default_arg;
48                    let raw = [Self::start(), a0, a1, a2, a3];
49                    let crc = $crate::crc::Crc7::calculate(&raw);
50                    let [r0, r1, r2, r3, r4] = raw;
51
52                    [r0, r1, r2, r3, r4, (crc.bits() << 1) | 1]
53                }
54
55                #[doc = "Gets the command type for the [" $cmd "]."]
56                #[inline]
57                pub const fn command_type(&self) -> $crate::command::CommandType {
58                    $cmd_type
59                }
60
61                #[doc = "Gets the command class for the [" $cmd "]."]
62                #[inline]
63                pub const fn command_class(&self) -> $crate::command::CommandClass {
64                    $cmd_class
65                }
66
67                #[doc = "Gets the command indnex field of the [" $cmd "]."]
68                pub const fn command_index(&self) -> u8 {
69                    self.raw_command_index() as u8
70                }
71
72                #[doc = "Gets the raw `argument` field of the [" $cmd "]."]
73                pub const fn argument_bits(&self) -> u32 {
74                    self.raw_argument()
75                }
76
77                #[doc = "Gets the `argument` field of the [" $cmd "]."]
78                pub const fn argument(&self) -> $crate::result::Result<$arg> {
79                    $arg::try_from_bits(self.raw_argument())
80                }
81
82                #[doc = "Sets the `argument` field of the [" $cmd "]."]
83                pub fn set_argument(&mut self, val: $arg) {
84                    self.set_raw_argument(val.bits())
85                }
86
87                #[doc = "Gets the `CRC7` field of the [" $cmd "]."]
88                pub const fn crc(&self) -> $crate::crc::Crc7 {
89                    $crate::crc::Crc7::from_bits(self.raw_crc() as u8)
90                }
91
92                #[doc = "Calculates and sets the `CRC7` field of the [" $cmd "]."]
93                pub fn calculate_crc(&mut self) -> $crate::crc::Crc7 {
94                    let crc = $crate::crc::Crc7::calculate(self.0[..Self::LEN - 1].as_ref());
95                    self.set_raw_crc(crc.bits() as u32);
96                    crc
97                }
98
99                #[doc = "Gets the expected response type for the [" $cmd "]."]
100                #[inline]
101                pub const fn response_type(&self) -> $crate::response::ResponseType {
102                    $res_type
103                }
104
105                #[doc = "Attempts to convert a byte slice into a [" $cmd "]."]
106                pub const fn try_from_bytes(val: &[u8]) -> $crate::result::Result<Self> {
107                    use $crate::result::Error;
108
109                    match val.len() {
110                        len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
111                        _ => {
112                            let crc = $crate::crc::Crc7::calculate(&[val[0], val[1], val[2], val[3], val[4]]);
113
114                            match Self([val[0], val[1], val[2], val[3], val[4], val[5]]) {
115                                cmd if cmd.start_bit() => Err(Error::invalid_field_variant("cmd::start_bit", 1)),
116                                cmd if !cmd.direction() => Err(Error::invalid_field_variant("cmd::direction", 0)),
117                                cmd if cmd.command_index() != Self::COMMAND_INDEX => Err(Error::invalid_field_variant(
118                                    "cmd::command_index",
119                                    cmd.command_index() as usize,
120                                )),
121                                cmd if cmd.argument().is_err() => Err(Error::invalid_field_variant(
122                                    "cmd::argument",
123                                    cmd.raw_argument() as usize,
124                                )),
125                                cmd if cmd.raw_crc() as u8 != crc.bits() => {
126                                    Err(Error::invalid_crc7(cmd.raw_crc() as u8, crc.bits()))
127                                }
128                                cmd if !cmd.end_bit() => Err(Error::invalid_field_variant("cmd::end_bit", 0)),
129                                cmd => Ok(cmd),
130                            }
131                        }
132                    }
133                }
134            }
135
136            impl Default for $cmd {
137                fn default() -> Self {
138                    Self::new()
139                }
140            }
141
142            impl From<$cmd> for [u8; $cmd::LEN] {
143                fn from(val: $cmd) -> Self {
144                    val.bytes()
145                }
146            }
147
148            impl TryFrom<&[u8]> for $cmd {
149                type Error = $crate::result::Error;
150
151                fn try_from(val: &[u8]) -> $crate::result::Result<Self> {
152                    Self::try_from_bytes(val)
153                }
154            }
155
156            impl<const N: usize> TryFrom<&[u8; N]> for $cmd {
157                type Error = $crate::result::Error;
158
159                fn try_from(val: &[u8; N]) -> $crate::result::Result<Self> {
160                    Self::try_from_bytes(val.as_ref())
161                }
162            }
163
164            impl<const N: usize> TryFrom<[u8; N]> for $cmd {
165                type Error = $crate::result::Error;
166
167                fn try_from(val: [u8; N]) -> $crate::result::Result<Self> {
168                    Self::try_from_bytes(val.as_ref())
169                }
170            }
171
172            #[cfg(test)]
173            pub mod tests {
174                use super::*;
175                use $crate::util::raw_with_crc;
176
177                #[test]
178                fn test_fields() {
179                    let new_arg = $arg::new();
180
181                    (1..=u32::BITS).map(|r| ((1u64 << r) - 1) as u32).for_each(|raw_arg| {
182                        let start = $cmd::start();
183                        let [arg0, arg1, arg2, arg3] = raw_arg.to_be_bytes();
184                        let (raw, crc) = raw_with_crc([start, arg0, arg1, arg2, arg3, 0]);
185
186                        match $arg::try_from_bits(raw_arg) {
187                            Ok(exp_arg) => {
188
189                                let mut exp_cmd = $cmd(raw);
190
191                                assert_eq!($cmd::try_from_bytes(&raw), Ok(exp_cmd));
192                                assert_eq!($cmd::try_from(&raw), Ok(exp_cmd));
193
194                                assert_eq!(exp_cmd.bytes(), raw);
195                                assert_eq!(exp_cmd.command_index(), $cmd::COMMAND_INDEX);
196                                assert_eq!(exp_cmd.argument(), Ok(exp_arg));
197                                assert_eq!(exp_cmd.crc(), crc);
198
199                                exp_cmd.set_argument(new_arg);
200                                assert_eq!(exp_cmd.argument(), Ok(new_arg));
201
202                                exp_cmd.set_argument(exp_arg);
203                                assert_eq!(exp_cmd.argument(), Ok(exp_arg));
204                            }
205                            Err(_err) => {
206                                let exp_err = Err($crate::result::Error::invalid_field_variant("cmd::argument", raw_arg as usize));
207
208                                assert_eq!($cmd::try_from_bytes(&raw), exp_err);
209                                assert_eq!($cmd::try_from(&raw), exp_err);
210                            }
211                        }
212                    });
213                }
214            }
215        }
216    };
217}
218
219/// Convenience macro to define a wrapper enum around command class variants.
220#[macro_export]
221macro_rules! command_enum {
222    (
223        $(#[$doc:meta])*
224        $cmd:ident {
225            default: $default_cmd:ident ($default_cmd_ty:expr),
226            $(
227                $(#[$doc_var:meta])*
228                $cmd_var:ident ($cmd_mod:ident :: $cmd_inner:ident),
229            )+
230        }
231    ) => {
232        $(#[$doc])*
233        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
234        pub enum $cmd {
235            $(
236                $(#[$doc_var])*
237                $cmd_var($cmd_mod::$cmd_inner),
238            )+
239        }
240
241        ::paste::paste! {
242            impl $cmd {
243                #[doc = "Represents the byte length of the [" $cmd "]."]
244                pub const LEN: usize = 6;
245
246                #[doc = "Creates a new [" $cmd "]."]
247                pub const fn new() -> Self {
248                    Self::$default_cmd($default_cmd_ty::new())
249                }
250
251                #[doc = "Gets the command index for the [" $cmd "]."]
252                pub const fn command_index(&self) -> u8 {
253                    match self {
254                        $(
255                            Self::$cmd_var(cmd) => cmd.command_index(),
256                        )+
257                    }
258                }
259
260                #[doc = "Gets the command type for the [" $cmd "]."]
261                pub const fn command_type(&self) -> $crate::command::CommandType {
262                    match self {
263                        $(
264                            Self::$cmd_var(cmd) => cmd.command_type(),
265                        )+
266                    }
267                }
268
269                #[doc = "Gets the response type for the [" $cmd "]."]
270                pub const fn response_type(&self) -> $crate::response::ResponseType {
271                    match self {
272                        $(
273                            Self::$cmd_var(cmd) => cmd.response_type(),
274                        )+
275                    }
276                }
277
278                #[doc = "Gets the raw command argument value for the [" $cmd "]."]
279                pub fn argument(&self) -> $crate::result::Result<u32> {
280                    match self {
281                        $(
282                            Self::$cmd_var(cmd) => cmd.argument().map(|arg| arg.bits()),
283                        )+
284                    }
285                }
286
287                #[doc = "Gets the `CRC7` field of the [" $cmd "]."]
288                pub const fn crc(&self) -> $crate::crc::Crc7 {
289                    match self {
290                        $(
291                            Self::$cmd_var(cmd) => cmd.crc(),
292                        )+
293                    }
294                }
295
296                #[doc = "Converts the [" $cmd "] into a byte array."]
297                pub const fn bytes(&self) -> [u8; Self::LEN] {
298                    match self {
299                        $(
300                            Self::$cmd_var(cmd) => cmd.bytes(),
301                        )+
302                    }
303                }
304            }
305
306            impl Default for $cmd {
307                fn default() -> Self {
308                    Self::new()
309                }
310            }
311
312            $(
313                impl From<$cmd_mod::$cmd_inner> for $cmd {
314                    fn from(val: $cmd_mod::$cmd_inner) -> Self {
315                        Self::$cmd_var(val)
316                    }
317                }
318
319                impl TryFrom<$cmd> for $cmd_mod::$cmd_inner {
320                    type Error = $crate::result::Error;
321
322                    fn try_from(val: $cmd) -> $crate::result::Result<Self> {
323                        match val {
324                            $cmd::$cmd_var(cmd) => Ok(cmd),
325                            _ => Err(Self::Error::invalid_field_variant("command", val.command_index() as usize)),
326                        }
327                    }
328                }
329            )+
330        }
331    };
332}