1use crate::response::ResponseType;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[non_exhaustive]
9pub enum DataDirection {
10 None,
12 Read,
14 Write,
16}
17
18impl DataDirection {
19 pub const fn is_none(self) -> bool {
21 matches!(self, DataDirection::None)
22 }
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct Command {
28 pub cmd: u8,
29 pub arg: u32,
30 pub resp_type: ResponseType,
31}
32
33impl Command {
34 pub const fn new(cmd: u8, arg: u32, resp_type: ResponseType) -> Self {
35 Self {
36 cmd,
37 arg,
38 resp_type,
39 }
40 }
41
42 pub const fn with_resp_type(self, resp_type: ResponseType) -> Self {
48 Self { resp_type, ..self }
49 }
50
51 pub fn index(&self) -> u8 {
53 self.cmd
54 }
55
56 pub fn argument(&self) -> u32 {
58 self.arg
59 }
60
61 pub const fn data_direction(&self) -> DataDirection {
69 match self.cmd {
70 17 | 18 => DataDirection::Read,
71 24 | 25 => DataDirection::Write,
72 _ => DataDirection::None,
73 }
74 }
75
76 pub const fn data_block_size(&self) -> Option<u32> {
84 match self.cmd {
85 17 | 18 | 24 | 25 => Some(512),
86 _ => None,
87 }
88 }
89
90 pub fn crc7(&self) -> u8 {
92 let mut crc: u8 = 0;
93 let token: u8 = 0x40 | (self.cmd & 0x3F);
95 crc = crc7_update(crc, token);
96 for byte in self.arg.to_be_bytes() {
97 crc = crc7_update(crc, byte);
98 }
99 (crc << 1) | 1 }
101
102 pub fn to_spi_bytes(&self) -> [u8; 6] {
104 let crc = self.crc7();
105 let token = 0x40 | (self.cmd & 0x3F);
106 let arg = self.arg.to_be_bytes();
107 [token, arg[0], arg[1], arg[2], arg[3], crc]
108 }
109}
110
111fn crc7_update(crc: u8, byte: u8) -> u8 {
112 let mut crc = crc;
113 let mut data = byte;
114 for _ in 0..8 {
115 crc <<= 1;
116 if (crc ^ data) & 0x80 != 0 {
117 crc ^= 0x89;
118 }
119 data <<= 1;
120 }
121 crc
122}
123
124pub const CMD0: Command = Command::new(0, 0, ResponseType::None);
130
131pub const CMD2: Command = Command::new(2, 0, ResponseType::R2);
133
134pub const CMD3_SD: Command = Command::new(3, 0, ResponseType::R6);
136pub fn cmd3_mmc(rca: u16) -> Command {
138 Command::new(3, (rca as u32) << 16, ResponseType::R1)
139}
140
141pub fn cmd4(dsr: u16) -> Command {
143 Command::new(4, (dsr as u32) << 16, ResponseType::None)
144}
145
146pub fn cmd6(arg: u32) -> Command {
148 Command::new(6, arg, ResponseType::R1)
149}
150
151pub fn cmd6_high_speed(switch: bool) -> Command {
157 cmd6_sd_access_mode(switch, 1)
158}
159
160pub fn cmd6_sd_access_mode(switch: bool, function: u8) -> Command {
166 let mode = if switch { 1u32 << 31 } else { 0 };
167 let groups = 0x00FF_FFF0u32 | u32::from(function & 0xF);
169 Command::new(6, mode | groups, ResponseType::R1)
170}
171
172pub fn cmd7(rca: u16) -> Command {
174 Command::new(7, (rca as u32) << 16, ResponseType::R1b)
175}
176
177pub fn cmd8(voltage: u8, check_pattern: u8) -> Command {
179 let arg = ((voltage as u32) << 8) | check_pattern as u32;
180 Command::new(8, arg, ResponseType::R7)
181}
182
183pub fn cmd9(rca: u16) -> Command {
185 Command::new(9, (rca as u32) << 16, ResponseType::R2)
186}
187
188pub fn cmd10(rca: u16) -> Command {
190 Command::new(10, (rca as u32) << 16, ResponseType::R2)
191}
192
193pub const CMD11: Command = Command::new(11, 0, ResponseType::R1);
200
201pub const CMD12: Command = Command::new(12, 0, ResponseType::R1b);
203
204pub fn cmd13(rca: u16) -> Command {
206 Command::new(13, (rca as u32) << 16, ResponseType::R1)
207}
208
209pub fn cmd16(block_len: u32) -> Command {
211 Command::new(16, block_len, ResponseType::R1)
212}
213
214pub fn cmd17(addr: u32) -> Command {
216 Command::new(17, addr, ResponseType::R1)
217}
218
219pub fn cmd18(addr: u32) -> Command {
221 Command::new(18, addr, ResponseType::R1)
222}
223
224pub fn cmd24(addr: u32) -> Command {
226 Command::new(24, addr, ResponseType::R1)
227}
228
229pub fn cmd25(addr: u32) -> Command {
231 Command::new(25, addr, ResponseType::R1)
232}
233
234pub const CMD19: Command = Command::new(19, 0, ResponseType::R1);
241
242pub const CMD21: Command = Command::new(21, 0, ResponseType::R1);
249
250pub const SD_TUNING_BLOCK_SIZE: u32 = 64;
252pub const MMC_TUNING_BLOCK_SIZE_4BIT: u32 = 64;
254pub const MMC_TUNING_BLOCK_SIZE_8BIT: u32 = 128;
256
257pub fn cmd32(addr: u32) -> Command {
259 Command::new(32, addr, ResponseType::R1)
260}
261
262pub fn cmd33(addr: u32) -> Command {
264 Command::new(33, addr, ResponseType::R1)
265}
266
267pub const CMD38: Command = Command::new(38, 0, ResponseType::R1b);
269
270pub fn cmd41(hcs: bool, voltage_window: u32) -> Command {
272 cmd41_with_s18r(hcs, voltage_window, false)
273}
274
275pub fn cmd41_with_s18r(hcs: bool, voltage_window: u32, s18r: bool) -> Command {
277 let arg = if hcs { 0x4000_0000 } else { 0 }
278 | if s18r { 1 << 24 } else { 0 }
279 | (voltage_window & 0x00FF_F800);
280 Command::new(41, arg, ResponseType::R3)
281}
282
283pub fn cmd55(rca: u16) -> Command {
285 Command::new(55, (rca as u32) << 16, ResponseType::R1)
286}
287
288pub const CMD58: Command = Command::new(58, 0, ResponseType::R3);
290
291pub fn cmd1(voltage_window: u32) -> Command {
295 Command::new(1, voltage_window, ResponseType::R3)
296}
297
298pub fn cmd6_mmc_switch(access: u8, index: u8, value: u8) -> Command {
306 let arg = ((access as u32) << 24) | ((index as u32) << 16) | ((value as u32) << 8);
307 Command::new(6, arg, ResponseType::R1b)
308}
309
310pub const CMD8_MMC: Command = Command::new(8, 0, ResponseType::R1);
317
318pub mod ext_csd {
321 pub const DEVICE_TYPE: usize = 196;
323 pub const HS_TIMING: usize = 185;
327 pub const BUS_WIDTH: usize = 183;
330 pub const SEC_COUNT: usize = 212;
332
333 pub mod device_type {
334 pub const HS_26: u8 = 1 << 0;
336 pub const HS_52: u8 = 1 << 1;
338 pub const HS200_18V: u8 = 1 << 4;
340 pub const HS200_12V: u8 = 1 << 5;
342 }
343}
344
345pub const CMD5: Command = Command::new(5, 0, ResponseType::R4);
349
350pub fn cmd52(write: bool, function: u8, raw: bool, addr: u32, data: u8) -> Command {
354 let arg = (write as u32) << 31
355 | ((function as u32) & 0x7) << 28
356 | (raw as u32) << 27
357 | (addr & 0x1_FFFF) << 9
358 | (data as u32);
359 Command::new(52, arg, ResponseType::R5)
360}
361
362pub fn cmd53(
367 write: bool,
368 function: u8,
369 block_mode: bool,
370 addr: u32,
371 op_code: bool,
372 count: u16,
373) -> Command {
374 let arg = (write as u32) << 31
375 | ((function as u32) & 0x7) << 28
376 | (block_mode as u32) << 27
377 | (op_code as u32) << 26
378 | (addr & 0x1_FFFF) << 9
379 | (count as u32 & 0x1FF);
380 Command::new(53, arg, ResponseType::R5)
381}
382
383#[cfg(test)]
384mod tests {
385 use super::*;
386
387 #[test]
388 fn test_cmd0_crc() {
389 let bytes = CMD0.to_spi_bytes();
390 assert_eq!(bytes[0], 0x40);
392 assert_eq!(bytes[5], 0x95);
393 }
394
395 #[test]
396 fn test_cmd8_spi_bytes() {
397 let cmd = cmd8(0x01, 0xAA);
398 let bytes = cmd.to_spi_bytes();
399 assert_eq!(bytes[0], 0x48); assert_eq!(bytes[1], 0x00);
401 assert_eq!(bytes[2], 0x00);
402 assert_eq!(bytes[3], 0x01);
403 assert_eq!(bytes[4], 0xAA);
404 }
405
406 #[test]
407 fn cmd52_encodes_full_17_bit_address() {
408 let cmd = cmd52(true, 1, false, 0x1_ABCD, 0x55);
409 let expected = (1u32 << 31) | (1u32 << 28) | (0x1_ABCDu32 << 9) | 0x55;
411 assert_eq!(cmd.arg, expected);
412 assert_eq!(cmd.cmd, 52);
413 }
414
415 #[test]
416 fn cmd53_encodes_full_17_bit_address_and_count() {
417 let cmd = cmd53(false, 2, true, 0x1_FFFF, true, 0x1FF);
418 let expected = (2u32 << 28) | (1u32 << 27) | (1u32 << 26) | (0x1_FFFFu32 << 9) | 0x1FF;
420 assert_eq!(cmd.arg, expected);
421 assert_eq!(cmd.cmd, 53);
422 }
423
424 #[test]
425 fn data_direction_classifies_block_commands() {
426 assert_eq!(cmd17(0).data_direction(), DataDirection::Read);
427 assert_eq!(cmd18(0).data_direction(), DataDirection::Read);
428 assert_eq!(cmd24(0).data_direction(), DataDirection::Write);
429 assert_eq!(cmd25(0).data_direction(), DataDirection::Write);
430 assert_eq!(cmd6(0).data_direction(), DataDirection::None);
433 assert_eq!(CMD0.data_direction(), DataDirection::None);
434 assert_eq!(CMD12.data_direction(), DataDirection::None);
435 assert!(CMD0.data_direction().is_none());
436 }
437
438 #[test]
439 fn data_block_size_reports_known_lengths() {
440 assert_eq!(cmd17(0).data_block_size(), Some(512));
441 assert_eq!(cmd18(0).data_block_size(), Some(512));
442 assert_eq!(cmd24(0).data_block_size(), Some(512));
443 assert_eq!(cmd25(0).data_block_size(), Some(512));
444 assert_eq!(cmd6(0).data_block_size(), None);
445 assert_eq!(CMD0.data_block_size(), None);
446 assert_eq!(CMD12.data_block_size(), None);
447 }
448
449 #[test]
450 fn cmd6_high_speed_arg_matches_spec() {
451 let switch = cmd6_high_speed(true);
452 assert_eq!(switch.cmd, 6);
453 assert_eq!(switch.arg, 0x80FF_FFF1);
454 let check = cmd6_high_speed(false);
455 assert_eq!(check.arg, 0x00FF_FFF1);
456 }
457
458 #[test]
459 fn cmd6_sd_access_mode_arg_selects_group1_function() {
460 let sdr104 = cmd6_sd_access_mode(true, 3);
461 assert_eq!(sdr104.cmd, 6);
462 assert_eq!(sdr104.arg, 0x80FF_FFF3);
463
464 let ddr50 = cmd6_sd_access_mode(false, 4);
465 assert_eq!(ddr50.arg, 0x00FF_FFF4);
466 }
467
468 #[test]
469 fn cmd41_with_s18r_sets_1v8_request_bit() {
470 let cmd = cmd41_with_s18r(true, 0xFF8000, true);
471 assert_eq!(cmd.arg, 0x4100_0000 | 0x00FF_8000);
472 }
473
474 #[test]
475 fn with_resp_type_overrides_only_resp_type() {
476 let original = cmd41(true, 0xFF8000);
477 let overridden = original.with_resp_type(ResponseType::R1);
478 assert_eq!(overridden.cmd, original.cmd);
479 assert_eq!(overridden.arg, original.arg);
480 assert_eq!(overridden.resp_type, ResponseType::R1);
481 assert_eq!(original.resp_type, ResponseType::R3);
482 }
483}