1use crate::response::ResponseType;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum DataDirection {
6 None,
8 Read,
10 Write,
12}
13
14impl DataDirection {
15 pub const fn is_none(self) -> bool {
17 matches!(self, DataDirection::None)
18 }
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct Command {
24 pub cmd: u8,
25 pub arg: u32,
26 pub resp_type: ResponseType,
27}
28
29impl Command {
30 pub const fn new(cmd: u8, arg: u32, resp_type: ResponseType) -> Self {
31 Self {
32 cmd,
33 arg,
34 resp_type,
35 }
36 }
37
38 pub const fn with_resp_type(self, resp_type: ResponseType) -> Self {
44 Self { resp_type, ..self }
45 }
46
47 pub fn index(&self) -> u8 {
49 self.cmd
50 }
51
52 pub fn argument(&self) -> u32 {
54 self.arg
55 }
56
57 pub const fn data_direction(&self) -> DataDirection {
65 match self.cmd {
66 17 | 18 => DataDirection::Read,
67 24 | 25 => DataDirection::Write,
68 _ => DataDirection::None,
69 }
70 }
71
72 pub const fn data_block_size(&self) -> Option<u32> {
80 match self.cmd {
81 17 | 18 | 24 | 25 => Some(512),
82 _ => None,
83 }
84 }
85
86 pub fn crc7(&self) -> u8 {
88 let mut crc: u8 = 0;
89 let token: u8 = 0x40 | (self.cmd & 0x3F);
91 crc = crc7_update(crc, token);
92 for byte in self.arg.to_be_bytes() {
93 crc = crc7_update(crc, byte);
94 }
95 (crc << 1) | 1 }
97
98 pub fn to_spi_bytes(&self) -> [u8; 6] {
100 let crc = self.crc7();
101 let token = 0x40 | (self.cmd & 0x3F);
102 let arg = self.arg.to_be_bytes();
103 [token, arg[0], arg[1], arg[2], arg[3], crc]
104 }
105}
106
107fn crc7_update(crc: u8, byte: u8) -> u8 {
108 let mut crc = crc;
109 let mut data = byte;
110 for _ in 0..8 {
111 crc <<= 1;
112 if (crc ^ data) & 0x80 != 0 {
113 crc ^= 0x89;
114 }
115 data <<= 1;
116 }
117 crc
118}
119
120pub const CMD0: Command = Command::new(0, 0, ResponseType::None);
126
127pub const CMD2: Command = Command::new(2, 0, ResponseType::R2);
129
130pub const CMD3_SD: Command = Command::new(3, 0, ResponseType::R6);
132pub fn cmd3_mmc(rca: u16) -> Command {
134 Command::new(3, (rca as u32) << 16, ResponseType::R1)
135}
136
137pub fn cmd4(dsr: u16) -> Command {
139 Command::new(4, (dsr as u32) << 16, ResponseType::None)
140}
141
142pub fn cmd6(arg: u32) -> Command {
144 Command::new(6, arg, ResponseType::R1)
145}
146
147pub fn cmd6_high_speed(switch: bool) -> Command {
153 cmd6_sd_access_mode(switch, 1)
154}
155
156pub fn cmd6_sd_access_mode(switch: bool, function: u8) -> Command {
162 let mode = if switch { 1u32 << 31 } else { 0 };
163 let groups = 0x00FF_FFF0u32 | u32::from(function & 0xF);
165 Command::new(6, mode | groups, ResponseType::R1)
166}
167
168pub fn cmd7(rca: u16) -> Command {
170 Command::new(7, (rca as u32) << 16, ResponseType::R1b)
171}
172
173pub fn cmd8(voltage: u8, check_pattern: u8) -> Command {
175 let arg = ((voltage as u32) << 8) | check_pattern as u32;
176 Command::new(8, arg, ResponseType::R7)
177}
178
179pub fn cmd9(rca: u16) -> Command {
181 Command::new(9, (rca as u32) << 16, ResponseType::R2)
182}
183
184pub fn cmd10(rca: u16) -> Command {
186 Command::new(10, (rca as u32) << 16, ResponseType::R2)
187}
188
189pub const CMD11: Command = Command::new(11, 0, ResponseType::R1);
196
197pub const CMD12: Command = Command::new(12, 0, ResponseType::R1b);
199
200pub fn cmd13(rca: u16) -> Command {
202 Command::new(13, (rca as u32) << 16, ResponseType::R1)
203}
204
205pub fn cmd16(block_len: u32) -> Command {
207 Command::new(16, block_len, ResponseType::R1)
208}
209
210pub fn cmd17(addr: u32) -> Command {
212 Command::new(17, addr, ResponseType::R1)
213}
214
215pub fn cmd18(addr: u32) -> Command {
217 Command::new(18, addr, ResponseType::R1)
218}
219
220pub fn cmd24(addr: u32) -> Command {
222 Command::new(24, addr, ResponseType::R1)
223}
224
225pub fn cmd25(addr: u32) -> Command {
227 Command::new(25, addr, ResponseType::R1)
228}
229
230pub const CMD19: Command = Command::new(19, 0, ResponseType::R1);
237
238pub const CMD21: Command = Command::new(21, 0, ResponseType::R1);
245
246pub const SD_TUNING_BLOCK_SIZE: u32 = 64;
248pub const MMC_TUNING_BLOCK_SIZE_4BIT: u32 = 64;
250pub const MMC_TUNING_BLOCK_SIZE_8BIT: u32 = 128;
252
253pub fn cmd32(addr: u32) -> Command {
255 Command::new(32, addr, ResponseType::R1)
256}
257
258pub fn cmd33(addr: u32) -> Command {
260 Command::new(33, addr, ResponseType::R1)
261}
262
263pub const CMD38: Command = Command::new(38, 0, ResponseType::R1b);
265
266pub fn cmd41(hcs: bool, voltage_window: u32) -> Command {
268 cmd41_with_s18r(hcs, voltage_window, false)
269}
270
271pub fn cmd41_with_s18r(hcs: bool, voltage_window: u32, s18r: bool) -> Command {
273 let arg = if hcs { 0x4000_0000 } else { 0 }
274 | if s18r { 1 << 24 } else { 0 }
275 | (voltage_window & 0x00FF_F800);
276 Command::new(41, arg, ResponseType::R3)
277}
278
279pub fn cmd55(rca: u16) -> Command {
281 Command::new(55, (rca as u32) << 16, ResponseType::R1)
282}
283
284pub const CMD58: Command = Command::new(58, 0, ResponseType::R3);
286
287pub fn cmd1(voltage_window: u32) -> Command {
291 Command::new(1, voltage_window, ResponseType::R3)
292}
293
294pub fn cmd6_mmc_switch(access: u8, index: u8, value: u8) -> Command {
302 let arg = ((access as u32) << 24) | ((index as u32) << 16) | ((value as u32) << 8);
303 Command::new(6, arg, ResponseType::R1b)
304}
305
306pub const CMD8_MMC: Command = Command::new(8, 0, ResponseType::R1);
313
314pub mod ext_csd {
317 pub const DEVICE_TYPE: usize = 196;
319 pub const HS_TIMING: usize = 185;
323 pub const BUS_WIDTH: usize = 183;
326 pub const SEC_COUNT: usize = 212;
328
329 pub mod device_type {
330 pub const HS_26: u8 = 1 << 0;
332 pub const HS_52: u8 = 1 << 1;
334 pub const HS200_18V: u8 = 1 << 4;
336 pub const HS200_12V: u8 = 1 << 5;
338 }
339}
340
341pub const CMD5: Command = Command::new(5, 0, ResponseType::R4);
345
346pub fn cmd52(write: bool, function: u8, raw: bool, addr: u32, data: u8) -> Command {
350 let arg = (write as u32) << 31
351 | ((function as u32) & 0x7) << 28
352 | (raw as u32) << 27
353 | (addr & 0x1_FFFF) << 9
354 | (data as u32);
355 Command::new(52, arg, ResponseType::R5)
356}
357
358pub fn cmd53(
363 write: bool,
364 function: u8,
365 block_mode: bool,
366 addr: u32,
367 op_code: bool,
368 count: u16,
369) -> Command {
370 let arg = (write as u32) << 31
371 | ((function as u32) & 0x7) << 28
372 | (block_mode as u32) << 27
373 | (op_code as u32) << 26
374 | (addr & 0x1_FFFF) << 9
375 | (count as u32 & 0x1FF);
376 Command::new(53, arg, ResponseType::R5)
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382
383 #[test]
384 fn test_cmd0_crc() {
385 let bytes = CMD0.to_spi_bytes();
386 assert_eq!(bytes[0], 0x40);
388 assert_eq!(bytes[5], 0x95);
389 }
390
391 #[test]
392 fn test_cmd8_spi_bytes() {
393 let cmd = cmd8(0x01, 0xAA);
394 let bytes = cmd.to_spi_bytes();
395 assert_eq!(bytes[0], 0x48); assert_eq!(bytes[1], 0x00);
397 assert_eq!(bytes[2], 0x00);
398 assert_eq!(bytes[3], 0x01);
399 assert_eq!(bytes[4], 0xAA);
400 }
401
402 #[test]
403 fn cmd52_encodes_full_17_bit_address() {
404 let cmd = cmd52(true, 1, false, 0x1_ABCD, 0x55);
405 let expected = (1u32 << 31) | (1u32 << 28) | (0x1_ABCDu32 << 9) | 0x55;
407 assert_eq!(cmd.arg, expected);
408 assert_eq!(cmd.cmd, 52);
409 }
410
411 #[test]
412 fn cmd53_encodes_full_17_bit_address_and_count() {
413 let cmd = cmd53(false, 2, true, 0x1_FFFF, true, 0x1FF);
414 let expected = (2u32 << 28) | (1u32 << 27) | (1u32 << 26) | (0x1_FFFFu32 << 9) | 0x1FF;
416 assert_eq!(cmd.arg, expected);
417 assert_eq!(cmd.cmd, 53);
418 }
419
420 #[test]
421 fn data_direction_classifies_block_commands() {
422 assert_eq!(cmd17(0).data_direction(), DataDirection::Read);
423 assert_eq!(cmd18(0).data_direction(), DataDirection::Read);
424 assert_eq!(cmd24(0).data_direction(), DataDirection::Write);
425 assert_eq!(cmd25(0).data_direction(), DataDirection::Write);
426 assert_eq!(cmd6(0).data_direction(), DataDirection::None);
429 assert_eq!(CMD0.data_direction(), DataDirection::None);
430 assert_eq!(CMD12.data_direction(), DataDirection::None);
431 assert!(CMD0.data_direction().is_none());
432 }
433
434 #[test]
435 fn data_block_size_reports_known_lengths() {
436 assert_eq!(cmd17(0).data_block_size(), Some(512));
437 assert_eq!(cmd18(0).data_block_size(), Some(512));
438 assert_eq!(cmd24(0).data_block_size(), Some(512));
439 assert_eq!(cmd25(0).data_block_size(), Some(512));
440 assert_eq!(cmd6(0).data_block_size(), None);
441 assert_eq!(CMD0.data_block_size(), None);
442 assert_eq!(CMD12.data_block_size(), None);
443 }
444
445 #[test]
446 fn cmd6_high_speed_arg_matches_spec() {
447 let switch = cmd6_high_speed(true);
448 assert_eq!(switch.cmd, 6);
449 assert_eq!(switch.arg, 0x80FF_FFF1);
450 let check = cmd6_high_speed(false);
451 assert_eq!(check.arg, 0x00FF_FFF1);
452 }
453
454 #[test]
455 fn cmd6_sd_access_mode_arg_selects_group1_function() {
456 let sdr104 = cmd6_sd_access_mode(true, 3);
457 assert_eq!(sdr104.cmd, 6);
458 assert_eq!(sdr104.arg, 0x80FF_FFF3);
459
460 let ddr50 = cmd6_sd_access_mode(false, 4);
461 assert_eq!(ddr50.arg, 0x00FF_FFF4);
462 }
463
464 #[test]
465 fn cmd41_with_s18r_sets_1v8_request_bit() {
466 let cmd = cmd41_with_s18r(true, 0xFF8000, true);
467 assert_eq!(cmd.arg, 0x4100_0000 | 0x00FF_8000);
468 }
469
470 #[test]
471 fn with_resp_type_overrides_only_resp_type() {
472 let original = cmd41(true, 0xFF8000);
473 let overridden = original.with_resp_type(ResponseType::R1);
474 assert_eq!(overridden.cmd, original.cmd);
475 assert_eq!(overridden.arg, original.arg);
476 assert_eq!(overridden.resp_type, ResponseType::R1);
477 assert_eq!(original.resp_type, ResponseType::R3);
478 }
479}