sdmmc_core/command/class/class5/cmd38/
arg.rs

1use crate::lib_bitfield;
2use crate::result::{Error, Result};
3
4lib_bitfield! {
5    /// Argumentument for CMD38.
6    pub Argument(u32): u16 {
7        /// Secure request.
8        ///
9        /// Only supported if the `SEC_ER_EN` bit is set.
10        pub secure: 31;
11        reserved0: 30, 16;
12        /// Force garbage collect request.
13        ///
14        /// Only supported if `SEC_GB_CL_END` is set.
15        pub force_garbage_collect: 15;
16        reserved1: 14, 2;
17        /// Discard enable.
18        discard_enable: 1;
19        /// Identify write blocks for erase (TRIM enable).
20        trim_enable: 0;
21    }
22}
23
24impl Argument {
25    /// Represents the byte length of the [Argument].
26    pub const LEN: usize = 4;
27    /// Represents the default value of the [Argument].
28    pub const DEFAULT: u32 = 0;
29
30    /// Creates a new [Argument].
31    pub const fn new() -> Self {
32        Self(Self::DEFAULT)
33    }
34
35    /// Gets whether the `DISCARD` function is configured.
36    ///
37    /// This is true when `TRIM` is disabled, and `DISCARD` is enabled.
38    #[inline]
39    pub const fn discard(&self) -> bool {
40        !self.trim_enable() && self.discard_enable()
41    }
42
43    /// Gets whether the `DISCARD` function is configured.
44    ///
45    /// This also clears the `TRIM` bit if `val` is true.
46    pub fn set_discard(&mut self, val: bool) {
47        self.set_discard_enable(val);
48
49        let trim = if val { !val } else { self.trim_enable() };
50
51        self.set_trim_enable(trim);
52    }
53
54    /// Gets whether the `TRIM` function is configured.
55    ///
56    /// This is true when `TRIM` is enabled, and `DISCARD` is enabled.
57    #[inline]
58    pub const fn trim(&self) -> bool {
59        self.trim_enable() && self.discard_enable()
60    }
61
62    /// Sets whether the `TRIM` function is configured.
63    ///
64    /// This also sets the `DISCARD` bit if `val` is true.
65    pub fn set_trim(&mut self, val: bool) {
66        self.set_trim_enable(val);
67
68        let discard = if val { val } else { self.discard_enable() };
69
70        self.set_discard_enable(discard);
71    }
72
73    /// Gets whether the `ERASE` function is configured.
74    ///
75    /// This is true when all [Argument] bits are zero.
76    pub const fn erase(&self) -> bool {
77        self.0 == Self::DEFAULT
78    }
79
80    /// Gets the `ERASE` function for the `CMD38` [Argument].
81    pub fn set_erase(&mut self) {
82        self.0 = Self::DEFAULT;
83    }
84
85    /// Attempts to convert a [`u32`] into an [Argument].
86    pub const fn try_from_bits(val: u32) -> Result<Self> {
87        match Self(val) {
88            a if a.reserved0() != 0 || a.reserved1() != 0 => Err(Error::invalid_field_variant(
89                "cmd::argument::reserved",
90                val as usize,
91            )),
92            a => Ok(a),
93        }
94    }
95
96    /// Converts the [Argument] into a byte array.
97    pub const fn bytes(&self) -> [u8; Self::LEN] {
98        self.0.to_be_bytes()
99    }
100
101    /// Attempts to convert a byte slice into an [`Argument`].
102    pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
103        match val.len() {
104            len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
105            _ => Self::try_from_bits(u32::from_be_bytes([val[0], val[1], val[2], val[3]])),
106        }
107    }
108}
109
110impl Default for Argument {
111    fn default() -> Self {
112        Self::new()
113    }
114}
115
116impl From<Argument> for u32 {
117    fn from(val: Argument) -> Self {
118        val.bits()
119    }
120}
121
122impl From<Argument> for [u8; Argument::LEN] {
123    fn from(val: Argument) -> Self {
124        val.bytes()
125    }
126}
127
128impl TryFrom<u32> for Argument {
129    type Error = Error;
130
131    fn try_from(val: u32) -> Result<Self> {
132        Self::try_from_bits(val)
133    }
134}
135
136impl TryFrom<&[u8]> for Argument {
137    type Error = Error;
138
139    fn try_from(val: &[u8]) -> Result<Self> {
140        Self::try_from_bytes(val)
141    }
142}
143
144impl<const N: usize> TryFrom<&[u8; N]> for Argument {
145    type Error = Error;
146
147    fn try_from(val: &[u8; N]) -> Result<Self> {
148        Self::try_from_bytes(val.as_ref())
149    }
150}
151
152impl<const N: usize> TryFrom<[u8; N]> for Argument {
153    type Error = Error;
154
155    fn try_from(val: [u8; N]) -> Result<Self> {
156        Self::try_from_bytes(val.as_ref())
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn test_fields() {
166        (1..=u32::BITS)
167            .map(|r| ((1u64 << r) - 1) as u32)
168            .for_each(|raw_arg| {
169                let raw = raw_arg.to_be_bytes();
170                let exp_addr = raw_arg;
171
172                match Argument::try_from_bits(raw_arg) {
173                    Ok(mut exp_arg) => {
174                        assert_eq!(Argument::try_from_bits(raw_arg), Ok(exp_arg));
175                        assert_eq!(Argument::try_from_bytes(raw.as_ref()), Ok(exp_arg));
176                        assert_eq!(Argument::try_from(raw_arg), Ok(exp_arg));
177                        assert_eq!(Argument::try_from(raw), Ok(exp_arg));
178                        assert_eq!(Argument::try_from(&raw), Ok(exp_arg));
179
180                        let exp_secure = (raw_arg & (1 << 31)) != 0;
181                        let exp_force_gc = (raw_arg & (1 << 15)) != 0;
182
183                        let exp_discard_en = (raw_arg & (1 << 1)) != 0;
184                        let exp_trim_en = (raw_arg & (1 << 0)) != 0;
185
186                        let exp_discard = exp_discard_en && !exp_trim_en;
187                        let exp_trim = exp_discard_en && exp_trim_en;
188
189                        let exp_erase = raw_arg == Argument::DEFAULT;
190
191                        assert_eq!(exp_arg.bits(), raw_arg);
192                        assert_eq!(exp_arg.bytes(), raw);
193
194                        assert_eq!(exp_arg.erase(), exp_erase);
195
196                        assert_eq!(exp_arg.secure(), exp_secure);
197                        test_field!(exp_arg, secure: 31);
198
199                        assert_eq!(exp_arg.force_garbage_collect(), exp_force_gc);
200                        test_field!(exp_arg, force_garbage_collect: 15);
201
202                        assert_eq!(exp_arg.discard(), exp_discard);
203                        assert_eq!(exp_arg.discard_enable(), exp_discard_en);
204
205                        assert_eq!(exp_arg.trim(), exp_trim);
206                        assert_eq!(exp_arg.trim_enable(), exp_trim_en);
207
208                        exp_arg.set_discard(true);
209                        assert!(exp_arg.discard());
210                        assert!(!exp_arg.trim_enable());
211
212                        exp_arg.set_discard(false);
213                        assert!(!exp_arg.discard());
214                        assert!(!exp_arg.trim_enable());
215
216                        exp_arg.set_trim(true);
217                        assert!(exp_arg.trim());
218                        assert!(exp_arg.discard_enable());
219
220                        exp_arg.set_trim(false);
221                        assert!(!exp_arg.trim());
222                        assert!(exp_arg.discard_enable());
223
224                        exp_arg.set_erase();
225                        assert_eq!(exp_arg.bits(), Argument::DEFAULT);
226                    }
227                    Err(_) => {
228                        let exp_err = Error::invalid_field_variant(
229                            "cmd::argument::reserved",
230                            raw_arg as usize,
231                        );
232
233                        assert_eq!(Argument::try_from_bits(raw_arg), Err(exp_err));
234                        assert_eq!(Argument::try_from_bytes(&raw), Err(exp_err));
235                        assert_eq!(Argument::try_from(raw_arg), Err(exp_err));
236                        assert_eq!(Argument::try_from(raw), Err(exp_err));
237                        assert_eq!(Argument::try_from(&raw), Err(exp_err));
238                    }
239                }
240            });
241    }
242}