sdmmc_core/command/class/class2/cmd20/
arg.rs

1use crate::lib_bitfield;
2use crate::result::{Error, Result};
3
4mod scc;
5mod vsc;
6mod vscc;
7
8pub use scc::*;
9pub use vsc::*;
10pub use vscc::*;
11
12lib_bitfield! {
13    /// Argument for CMD20.
14    pub Argument(u32): u32 {
15        raw_scc: 31, 28;
16        raw_vsc: 27;
17        raw_cnt_id: 26, 24;
18        /// Data block address.
19        ///
20        /// # Note
21        ///
22        /// Only valid for Video speed mode.
23        pub address: 21, 0;
24    }
25}
26
27impl Argument {
28    /// Represents the byte length of the [Argument].
29    pub const LEN: usize = 4;
30    /// Represents the default value of the [Argument].
31    pub const DEFAULT: u32 = 0;
32
33    /// Creates a new [Argument].
34    pub const fn new() -> Self {
35        Self(Self::DEFAULT)
36    }
37
38    /// Gets the [VideoSpeedClass] in Video speed mode.
39    pub const fn vsc(&self) -> VideoSpeedClass {
40        VideoSpeedClass::from_bool(self.raw_vsc())
41    }
42
43    /// Gets the [VideoSpeedClass] in Video speed mode.
44    pub fn set_vsc(&mut self, val: VideoSpeedClass) {
45        self.set_raw_vsc(val.into_bool());
46    }
47
48    /// Gets the [SpeedClassControl] in legacy and UHS mode.
49    ///
50    /// # Note
51    ///
52    /// Only valid for [Legacy UHS](VideoSpeedClass::LegacyUHS) speed mode.
53    pub const fn scc(&self) -> Result<SpeedClassControl> {
54        match self.vsc() {
55            VideoSpeedClass::LegacyUHS => SpeedClassControl::from_raw(self.raw_scc() as u8),
56            VideoSpeedClass::Video => Err(Error::invalid_field_variant(
57                "cmd::argument::vsc",
58                self.raw_vsc() as usize,
59            )),
60        }
61    }
62
63    /// Sets the [SpeedClassControl] in legacy and UHS mode.
64    ///
65    /// # Note
66    ///
67    /// Changes the [VideoSpeedClass] to legacy UHS mode.
68    pub fn set_scc(&mut self, val: SpeedClassControl) {
69        self.set_vsc(VideoSpeedClass::LegacyUHS);
70        self.set_raw_scc(val.into_raw() as u32);
71    }
72
73    /// Gets the [VideoSpeedClassControl] for Video speed mode.
74    ///
75    /// # Note
76    ///
77    /// Only valid for [Video](VideoSpeedClass::LegacyUHS) speed mode.
78    pub const fn vscc(&self) -> Result<VideoSpeedClassControl> {
79        match self.vsc() {
80            VideoSpeedClass::LegacyUHS => Err(Error::invalid_field_variant(
81                "cmd::argument::vsc",
82                self.raw_vsc() as usize,
83            )),
84            VideoSpeedClass::Video => VideoSpeedClassControl::from_raw(self.raw_scc() as u8),
85        }
86    }
87
88    /// Sets the [VideoSpeedClassControl] for Video speed mode.
89    ///
90    /// # Note
91    ///
92    /// Changes the [VideoSpeedClass] to video mode.
93    pub fn set_vscc(&mut self, val: VideoSpeedClassControl) {
94        self.set_vsc(VideoSpeedClass::Video);
95        self.set_raw_scc(val.into_raw() as u32);
96    }
97
98    /// Gets the Count/ID field (based on function).
99    ///
100    /// # Note
101    ///
102    /// Only valid for [Video](VideoSpeedClass::LegacyUHS) speed mode.
103    pub const fn cnt_id(&self) -> u8 {
104        self.raw_cnt_id() as u8
105    }
106
107    /// Sets the Count/ID field (based on function).
108    ///
109    /// # Note
110    ///
111    /// Only valid for [Video](VideoSpeedClass::LegacyUHS) speed mode.
112    pub fn set_cnt_id(&mut self, val: u8) {
113        self.set_raw_cnt_id(val as u32);
114    }
115
116    /// Attempts to convert a [`u32`] into an [Argument].
117    pub const fn try_from_bits(val: u32) -> Result<Self> {
118        match Self(val) {
119            a if !a.raw_vsc() && a.scc().is_err() => Err(Error::invalid_field_variant(
120                "cmd::argument::scc",
121                a.raw_scc() as usize,
122            )),
123            a if a.raw_vsc() && a.vscc().is_err() => Err(Error::invalid_field_variant(
124                "cmd::argument::vscc",
125                a.raw_scc() as usize,
126            )),
127            a => Ok(a),
128        }
129    }
130
131    /// Converts the [Argument] into a byte array.
132    pub const fn bytes(&self) -> [u8; Self::LEN] {
133        self.0.to_be_bytes()
134    }
135
136    /// Attempts to convert a byte slice into an [Argument].
137    pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
138        match val.len() {
139            len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
140            _ => Self::try_from_bits(u32::from_be_bytes([val[0], val[1], val[2], val[3]])),
141        }
142    }
143}
144
145impl Default for Argument {
146    fn default() -> Self {
147        Self::new()
148    }
149}
150
151impl From<Argument> for u32 {
152    fn from(val: Argument) -> Self {
153        val.bits()
154    }
155}
156
157impl From<Argument> for [u8; Argument::LEN] {
158    fn from(val: Argument) -> Self {
159        val.bytes()
160    }
161}
162
163impl TryFrom<u32> for Argument {
164    type Error = Error;
165
166    fn try_from(val: u32) -> Result<Self> {
167        Self::try_from_bits(val)
168    }
169}
170
171impl TryFrom<&[u8]> for Argument {
172    type Error = Error;
173
174    fn try_from(val: &[u8]) -> Result<Self> {
175        Self::try_from_bytes(val)
176    }
177}
178
179impl<const N: usize> TryFrom<&[u8; N]> for Argument {
180    type Error = Error;
181
182    fn try_from(val: &[u8; N]) -> Result<Self> {
183        Self::try_from_bytes(val.as_ref())
184    }
185}
186
187impl<const N: usize> TryFrom<[u8; N]> for Argument {
188    type Error = Error;
189
190    fn try_from(val: [u8; N]) -> Result<Self> {
191        Self::try_from_bytes(val.as_ref())
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_fields() {
201        let mut arg = Argument::new();
202
203        assert_eq!(arg.address(), 0);
204        assert_eq!(arg.cnt_id(), 0);
205
206        assert_eq!(arg.vsc(), VideoSpeedClass::LegacyUHS);
207        arg.set_vsc(VideoSpeedClass::Video);
208        assert_eq!(arg.vsc(), VideoSpeedClass::Video);
209        arg.set_vsc(VideoSpeedClass::LegacyUHS);
210
211        assert_eq!(arg.scc(), Ok(SpeedClassControl::StartRecording));
212
213        arg.set_scc(SpeedClassControl::UpdateDIR);
214        assert_eq!(arg.scc(), Ok(SpeedClassControl::UpdateDIR));
215        assert_eq!(
216            arg.bits(),
217            (SpeedClassControl::UpdateDIR.into_raw() as u32) << 28
218        );
219
220        arg.set_scc(SpeedClassControl::StartRecording);
221        assert_eq!(arg.scc(), Ok(SpeedClassControl::StartRecording));
222
223        [
224            VideoSpeedClassControl::UpdateDIR,
225            VideoSpeedClassControl::UpdateCI,
226            VideoSpeedClassControl::SuspendRecording,
227            VideoSpeedClassControl::ResumeRecording,
228            VideoSpeedClassControl::SetFreeAU,
229            VideoSpeedClassControl::ReleaseDIR,
230        ]
231        .into_iter()
232        .for_each(|exp_vscc| {
233            arg.set_vscc(exp_vscc);
234            assert_eq!(arg.vscc(), Ok(exp_vscc));
235            assert_eq!(arg.bits() & (0xf << 28), (exp_vscc.into_raw() as u32) << 28);
236        });
237        arg.set_vscc(VideoSpeedClassControl::StartRecording);
238
239        (1..=21u32)
240            .map(|r| ((1u32 << r) - 1))
241            .for_each(|exp_address| {
242                let address = 0x3a_5555;
243                arg.set_address(address);
244
245                assert_eq!(arg.address(), address);
246                assert_eq!(arg.bits() & 0x3f_ffff, address);
247            });
248
249        (1..=u32::BITS)
250            .map(|r| ((1u64 << r) - 1) as u32)
251            .for_each(|raw_arg| {
252                let raw = raw_arg.to_be_bytes();
253                let raw_scc = ((raw_arg & (0xfu32 << 28)) >> 28) as u8;
254                let raw_vsc = ((raw_arg & (1u32 << 27)) >> 27) != 0;
255                let exp_arg = Argument(raw_arg);
256
257                match (
258                    VideoSpeedClass::from_bool(raw_vsc),
259                    SpeedClassControl::from_raw(raw_scc),
260                    VideoSpeedClassControl::from_raw(raw_scc),
261                ) {
262                    (vsc @ VideoSpeedClass::LegacyUHS, Ok(_), _)
263                    | (vsc @ VideoSpeedClass::Video, _, Ok(_)) => {
264                        assert_eq!(Argument::try_from_bits(raw_arg), Ok(exp_arg));
265                        assert_eq!(Argument::try_from_bytes(raw.as_ref()), Ok(exp_arg));
266                        assert_eq!(Argument::try_from(raw_arg), Ok(exp_arg));
267                        assert_eq!(Argument::try_from(raw), Ok(exp_arg));
268                        assert_eq!(Argument::try_from(&raw), Ok(exp_arg));
269
270                        assert_eq!(exp_arg.bits(), raw_arg);
271                        assert_eq!(exp_arg.bytes(), raw);
272
273                        match vsc {
274                            VideoSpeedClass::LegacyUHS => {
275                                let exp_scc = SpeedClassControl::from_raw(raw_scc).unwrap();
276
277                                assert_eq!(exp_arg.vsc(), vsc);
278                                assert_eq!(exp_arg.scc(), Ok(exp_scc));
279                                assert_eq!(
280                                    exp_arg.vscc(),
281                                    Err(Error::invalid_field_variant(
282                                        "cmd::argument::vsc",
283                                        vsc as usize
284                                    ))
285                                );
286                            }
287                            VideoSpeedClass::Video => {
288                                let exp_vscc = VideoSpeedClassControl::from_raw(raw_scc).unwrap();
289
290                                assert_eq!(exp_arg.vsc(), vsc);
291                                assert_eq!(
292                                    exp_arg.scc(),
293                                    Err(Error::invalid_field_variant(
294                                        "cmd::argument::vsc",
295                                        vsc as usize
296                                    ))
297                                );
298                                assert_eq!(exp_arg.vscc(), Ok(exp_vscc));
299                            }
300                        }
301                    }
302                    (vsc @ VideoSpeedClass::LegacyUHS, Err(_), _)
303                    | (vsc @ VideoSpeedClass::Video, _, Err(_)) => {
304                        let exp_err = match vsc {
305                            VideoSpeedClass::LegacyUHS => {
306                                Error::invalid_field_variant("cmd::argument::scc", raw_scc as usize)
307                            }
308                            VideoSpeedClass::Video => Error::invalid_field_variant(
309                                "cmd::argument::vscc",
310                                raw_scc as usize,
311                            ),
312                        };
313
314                        assert_eq!(Argument::try_from_bits(raw_arg), Err(exp_err));
315                        assert_eq!(Argument::try_from_bytes(raw.as_ref()), Err(exp_err));
316                        assert_eq!(Argument::try_from(raw_arg), Err(exp_err));
317                        assert_eq!(Argument::try_from(raw), Err(exp_err));
318                        assert_eq!(Argument::try_from(&raw), Err(exp_err));
319                    }
320                }
321            });
322    }
323}