Skip to main content

sdmmc_protocol/
response.rs

1use crate::error::{CardError, Error, ErrorContext, Phase};
2
3/// SD/MMC response types
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum ResponseType {
6    /// No response
7    None,
8    /// R1: Standard response (48-bit)
9    R1,
10    /// R1b: R1 with busy signal
11    R1b,
12    /// R2: CID/CSD register (136-bit)
13    R2,
14    /// R3: OCR register (48-bit)
15    R3,
16    /// R4: SDIO OCR (48-bit)
17    R4,
18    /// R5: SDIO RW (48-bit)
19    R5,
20    /// R6: Published RCA (48-bit, SD)
21    R6,
22    /// R7: Card interface condition (48-bit)
23    R7,
24}
25
26/// Parsed response from the card
27#[derive(Debug, Clone, Copy)]
28pub enum Response {
29    None,
30    R1(R1Response),
31    R1b(R1Response),
32    R2([u8; 16]),
33    R3(OcrResponse),
34    R4(SdioOcrResponse),
35    R5(SdioRwResponse),
36    R6(RcaResponse),
37    R7(IfCondResponse),
38}
39
40/// R1: Standard response — contains status bits
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub struct R1Response {
43    pub raw: u32,
44}
45
46impl R1Response {
47    /// Parse a native (SDIO/SDHCI) 32-bit R1 response.
48    ///
49    /// Card error bits live in bits 19..=24. If any error bit is set this
50    /// returns `Err(Error::CardError(..))`. Otherwise the raw value is
51    /// preserved.
52    pub fn from_native_raw(raw: u32) -> Result<Self, Error> {
53        let err_bits = ((raw >> 19) & 0x3F) as u8;
54        if err_bits != 0 {
55            return Err(Error::CardError(decode_native_card_error(err_bits)));
56        }
57        Ok(Self { raw })
58    }
59
60    /// Parse a single-byte SPI R1 response.
61    ///
62    /// SPI R1 has a fixed `0` start bit (the high bit must be clear). The
63    /// remaining bits encode informational state (idle, erase reset) and
64    /// soft error flags (illegal command, CRC error, ...). Because some flags
65    /// — especially `illegal_command` — are *expected* during initialization
66    /// (e.g. CMD8 on SD v1 cards), this function does NOT itself convert
67    /// flag bits into `Err`. Callers should inspect the helpers
68    /// ([`R1Response::illegal_command`] etc.) to decide what to do.
69    ///
70    /// Returns `Err(Error::BadResponse(_))` when the high bit is set, which
71    /// indicates a malformed response or that no R1 byte arrived.
72    pub fn from_spi_byte(byte: u8) -> Result<Self, Error> {
73        if byte & 0x80 != 0 {
74            return Err(Error::BadResponse(ErrorContext::new(Phase::ResponseWait)));
75        }
76        Ok(Self { raw: byte as u32 })
77    }
78
79    /// Decode error flag bits in a SPI R1 response into a [`CardError`].
80    ///
81    /// Returns `None` when no error bits are set. Only meaningful for values
82    /// produced by [`R1Response::from_spi_byte`]; native R1 layouts use a
83    /// different bit mapping and report errors directly through
84    /// [`R1Response::from_native_raw`].
85    pub fn spi_card_error(&self) -> Option<CardError> {
86        let bits = (self.raw as u8) & 0b0111_1110;
87        if bits == 0 {
88            None
89        } else {
90            Some(decode_spi_card_error(bits))
91        }
92    }
93
94    /// Backwards-compatible parser. Prefer [`R1Response::from_native_raw`] for
95    /// SDIO/native transports and [`R1Response::from_spi_byte`] for SPI mode.
96    ///
97    /// This is retained because external code may still call it. The behavior
98    /// matches `from_native_raw` for values larger than `0xFF` and treats
99    /// smaller values as raw SPI bytes (decoding their error bits as such).
100    #[deprecated(note = "use from_native_raw or from_spi_byte instead")]
101    pub fn from_raw(raw: u32) -> Result<Self, Error> {
102        if raw > 0xFF {
103            Self::from_native_raw(raw)
104        } else {
105            Self::from_spi_byte(raw as u8)
106        }
107    }
108
109    /// Card is in idle state
110    pub fn idle(&self) -> bool {
111        self.raw & (1 << 0) != 0
112    }
113
114    /// Erase reset
115    pub fn erase_reset(&self) -> bool {
116        self.raw & (1 << 1) != 0
117    }
118
119    /// Illegal command
120    pub fn illegal_command(&self) -> bool {
121        self.raw & (1 << 2) != 0
122    }
123
124    /// Command CRC failed
125    pub fn command_crc_failed(&self) -> bool {
126        self.raw & (1 << 3) != 0
127    }
128
129    /// Current state of the card state machine (bits 12:15).
130    ///
131    /// Only meaningful for native (SDIO) R1 responses; SPI R1 bytes do not
132    /// encode card state.
133    pub fn current_state(&self) -> CardState {
134        match ((self.raw >> 9) & 0xF) as u8 {
135            0 => CardState::Idle,
136            1 => CardState::Ready,
137            2 => CardState::Identification,
138            3 => CardState::Standby,
139            4 => CardState::Transfer,
140            5 => CardState::SendingData,
141            6 => CardState::ReceiveData,
142            7 => CardState::Programming,
143            8 => CardState::Disconnect,
144            other => CardState::Reserved(other),
145        }
146    }
147
148    /// Card is locked (native R1 only)
149    pub fn card_is_locked(&self) -> bool {
150        self.raw & (1 << 19) != 0
151    }
152
153    /// `READY_FOR_DATA` (bit 8): card buffer is empty and the next data
154    /// transfer can be issued. Used after R1b commands (CMD7, CMD12,
155    /// MMC CMD6 SWITCH) to know when the busy line has cleared.
156    ///
157    /// Only meaningful for native (SDIO) R1 responses.
158    pub fn ready_for_data(&self) -> bool {
159        self.raw & (1 << 8) != 0
160    }
161
162    /// `SWITCH_ERROR` (bit 7): the previous MMC CMD6 SWITCH was rejected
163    /// (e.g. invalid EXT_CSD field, value out of range). Surfaces here
164    /// because CMD6 itself returns R1b with this bit, but most error
165    /// reporters hide bits 0..15.
166    pub fn switch_error(&self) -> bool {
167        self.raw & (1 << 7) != 0
168    }
169}
170
171/// Card state machine states
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub enum CardState {
174    Idle,
175    Ready,
176    Identification,
177    Standby,
178    Transfer,
179    SendingData,
180    ReceiveData,
181    Programming,
182    Disconnect,
183    Reserved(u8),
184}
185
186/// OCR register (R3/CMD58 response)
187#[derive(Debug, Clone, Copy)]
188pub struct OcrResponse {
189    pub raw: u32,
190}
191
192impl OcrResponse {
193    pub fn from_raw(raw: u32) -> Self {
194        Self { raw }
195    }
196
197    /// Card power up status — true if card has completed power-up
198    pub fn card_powered_up(&self) -> bool {
199        self.raw & (1 << 31) != 0
200    }
201
202    /// Card Capacity Status (CCS): true = SDHC/SDXC, false = SDSC
203    pub fn ccs(&self) -> bool {
204        self.raw & (1 << 30) != 0
205    }
206
207    /// Supported voltage range (bits 23:0)
208    pub fn voltage_window(&self) -> u32 {
209        self.raw & 0x00FF_FF00
210    }
211
212    /// Supports 3.5–3.6V
213    pub fn vdd_35_36(&self) -> bool {
214        self.raw & (1 << 23) != 0
215    }
216
217    /// Supports 3.4–3.5V
218    pub fn vdd_34_35(&self) -> bool {
219        self.raw & (1 << 22) != 0
220    }
221
222    /// Supports 3.3–3.4V
223    pub fn vdd_33_34(&self) -> bool {
224        self.raw & (1 << 21) != 0
225    }
226
227    /// Supports 3.2–3.3V
228    pub fn vdd_32_33(&self) -> bool {
229        self.raw & (1 << 20) != 0
230    }
231
232    /// Supports 2.7–3.6V (typical operating range)
233    pub fn supports_2v7_to_3v6(&self) -> bool {
234        self.raw & 0x00FF_8000 != 0
235    }
236
237    /// UHS-II supported
238    pub fn uhs2(&self) -> bool {
239        self.raw & (1 << 29) != 0
240    }
241
242    /// Switching to 1.8 V was accepted during SD ACMD41 negotiation.
243    pub fn s18a(&self) -> bool {
244        self.raw & (1 << 24) != 0
245    }
246}
247
248/// R6: Published RCA response
249#[derive(Debug, Clone, Copy)]
250pub struct RcaResponse {
251    pub raw: u32,
252}
253
254impl RcaResponse {
255    pub fn from_raw(raw: u32) -> Self {
256        Self { raw }
257    }
258
259    /// Relative card address (bits 31:16)
260    pub fn rca(&self) -> u16 {
261        ((self.raw >> 16) & 0xFFFF) as u16
262    }
263
264    /// Status bits (bits 15:0) — subset of R1 status
265    pub fn status(&self) -> u16 {
266        (self.raw & 0xFFFF) as u16
267    }
268}
269
270/// R7: Interface condition response
271#[derive(Debug, Clone, Copy)]
272pub struct IfCondResponse {
273    pub raw: u32,
274}
275
276impl IfCondResponse {
277    pub fn from_raw(raw: u32) -> Self {
278        Self { raw }
279    }
280
281    /// Supported voltage (bits 11:8)
282    pub fn voltage(&self) -> u8 {
283        ((self.raw >> 8) & 0xF) as u8
284    }
285
286    /// Echo-back check pattern (bits 7:0)
287    pub fn check_pattern(&self) -> u8 {
288        (self.raw & 0xFF) as u8
289    }
290
291    /// Verify response matches expected voltage and pattern
292    pub fn verify(&self, voltage: u8, pattern: u8) -> bool {
293        self.voltage() == voltage && self.check_pattern() == pattern
294    }
295}
296
297/// CSD register (CMD9 response, raw 16 bytes MSB-first as delivered by both
298/// SPI and SDIO transports).
299#[derive(Debug, Clone, Copy)]
300pub struct CsdResponse {
301    pub raw: [u8; 16],
302}
303
304impl CsdResponse {
305    pub fn from_raw(raw: [u8; 16]) -> Self {
306        Self { raw }
307    }
308
309    /// CSD structure version: 0 = v1 (SDSC), 1 = v2 (SDHC/SDXC), 2 = v3 (SDUC)
310    pub fn version(&self) -> u8 {
311        (self.raw[0] >> 6) & 0x03
312    }
313
314    /// User-data capacity in 512-byte blocks.
315    ///
316    /// Returns `None` for unknown / unsupported CSD structures (e.g. SDUC v3,
317    /// which encodes a 28-bit C_SIZE that does not fit the v2 formula).
318    pub fn capacity_blocks(&self) -> Option<u64> {
319        match self.version() {
320            0 => Some(self.csd_v1_capacity_blocks()),
321            1 => Some(self.csd_v2_capacity_blocks()),
322            _ => None,
323        }
324    }
325
326    fn csd_v1_capacity_blocks(&self) -> u64 {
327        // CSD v1 fields (bit numbering as in SD spec, MSB = bit 127):
328        //   READ_BL_LEN [83:80]   — log2 of read block length
329        //   C_SIZE      [73:62]   — 12-bit
330        //   C_SIZE_MULT [49:47]   — 3-bit
331        // capacity_bytes = (C_SIZE + 1) * 2^(C_SIZE_MULT + 2) * 2^READ_BL_LEN
332        let read_bl_len = (self.raw[5] & 0x0F) as u32;
333        let c_size = (((self.raw[6] & 0x03) as u32) << 10)
334            | ((self.raw[7] as u32) << 2)
335            | ((self.raw[8] as u32) >> 6);
336        let c_size_mult = (((self.raw[9] & 0x03) as u32) << 1) | ((self.raw[10] as u32) >> 7);
337        let mult = 1u64 << (c_size_mult + 2);
338        let block_len = 1u64 << read_bl_len;
339        let bytes = (c_size as u64 + 1) * mult * block_len;
340        bytes / 512
341    }
342
343    fn csd_v2_capacity_blocks(&self) -> u64 {
344        // CSD v2 (SDHC/SDXC):
345        //   C_SIZE [69:48] — 22-bit
346        //   capacity_bytes = (C_SIZE + 1) * 512 KiB
347        //   capacity_blocks = (C_SIZE + 1) * 1024
348        let c_size = (((self.raw[7] & 0x3F) as u32) << 16)
349            | ((self.raw[8] as u32) << 8)
350            | (self.raw[9] as u32);
351        (c_size as u64 + 1) * 1024
352    }
353}
354
355/// CID register (CMD2/CMD10 response). Identifies the card's manufacturer,
356/// product, serial number, and manufacturing date.
357///
358/// Field layout follows SD Physical Layer spec section 5.2; only SD cards are
359/// decoded here. eMMC uses a different field layout and is not supported.
360#[derive(Debug, Clone, Copy)]
361pub struct CidResponse {
362    pub raw: [u8; 16],
363}
364
365impl CidResponse {
366    pub fn from_raw(raw: [u8; 16]) -> Self {
367        Self { raw }
368    }
369
370    /// Manufacturer ID (MID) — 8-bit code assigned by the SD Association.
371    pub fn manufacturer_id(&self) -> u8 {
372        self.raw[0]
373    }
374
375    /// OEM/Application ID (OID) — two ASCII characters identifying the card
376    /// OEM. Returned as a `[u8; 2]`; bytes outside printable ASCII are
377    /// preserved verbatim so callers can detect non-conforming firmware.
378    pub fn oem_id(&self) -> [u8; 2] {
379        [self.raw[1], self.raw[2]]
380    }
381
382    /// Product name (PNM) — 5 ASCII characters.
383    pub fn product_name(&self) -> [u8; 5] {
384        [
385            self.raw[3],
386            self.raw[4],
387            self.raw[5],
388            self.raw[6],
389            self.raw[7],
390        ]
391    }
392
393    /// Product revision (PRV) as a `(major, minor)` pair, both 4-bit BCD.
394    pub fn product_revision(&self) -> (u8, u8) {
395        (self.raw[8] >> 4, self.raw[8] & 0x0F)
396    }
397
398    /// Product serial number (PSN) — 32-bit big-endian.
399    pub fn serial_number(&self) -> u32 {
400        u32::from_be_bytes([self.raw[9], self.raw[10], self.raw[11], self.raw[12]])
401    }
402
403    /// Manufacturing date as `(year, month)` where year is the absolute
404    /// 4-digit year (SD spec offsets year by 2000).
405    ///
406    /// Layout: bits 19:8 of bytes 13..=14 hold the date — 12 bits split as
407    /// year (8 bits) and month (4 bits).
408    pub fn manufacture_date(&self) -> (u16, u8) {
409        let year = ((self.raw[13] & 0x0F) << 4) | (self.raw[14] >> 4);
410        let month = self.raw[14] & 0x0F;
411        (2000 + year as u16, month)
412    }
413}
414
415/// 64-byte SD function-switch status, returned in the data phase of CMD6.
416///
417/// See SD Physical Layer spec section 4.3.10 (Switch Function). Field
418/// numbering uses the spec's bit-435..=0 convention but accessors here are
419/// expressed in byte offsets within `raw[0..64]` for clarity.
420#[derive(Debug, Clone, Copy)]
421pub struct SwitchStatus {
422    pub raw: [u8; 64],
423}
424
425impl SwitchStatus {
426    pub fn from_raw(raw: [u8; 64]) -> Self {
427        Self { raw }
428    }
429
430    /// Selected function for `group` (1-based, 1..=6) after a switch
431    /// operation. `0xF` means the group is not supported by the card.
432    ///
433    /// Group 1 selection lives in the low nibble of byte 16; group 2 in the
434    /// high nibble of the same byte; group 3 in the low nibble of byte 15;
435    /// and so on, paired big-endian over bytes 14..=16.
436    pub fn selected_function(&self, group: u8) -> u8 {
437        match group {
438            1 => self.raw[16] & 0x0F,
439            2 => self.raw[16] >> 4,
440            3 => self.raw[15] & 0x0F,
441            4 => self.raw[15] >> 4,
442            5 => self.raw[14] & 0x0F,
443            6 => self.raw[14] >> 4,
444            _ => 0xF,
445        }
446    }
447
448    /// Returns true iff group 1 reports high-speed (function 1) selected.
449    pub fn high_speed_active(&self) -> bool {
450        self.selected_function(1) == 1
451    }
452
453    /// Returns true iff SD access-mode group 1 advertises `function`.
454    ///
455    /// The support bitmap for group 1 is carried in byte 13 in the 64-byte
456    /// switch status block; bit `n` means function `n` is selectable.
457    pub fn access_mode_supported(&self, function: u8) -> bool {
458        function < 8 && (self.raw[13] & (1 << function)) != 0
459    }
460}
461
462/// SDIO OCR (R4/CMD5 response)
463#[derive(Debug, Clone, Copy)]
464pub struct SdioOcrResponse {
465    pub raw: u32,
466}
467
468impl SdioOcrResponse {
469    pub fn from_raw(raw: u32) -> Self {
470        Self { raw }
471    }
472
473    /// Number of I/O functions (bits 27:28)
474    pub fn io_functions(&self) -> u8 {
475        ((self.raw >> 28) & 0x7) as u8
476    }
477
478    /// Memory present
479    pub fn memory_present(&self) -> bool {
480        self.raw & (1 << 27) != 0
481    }
482
483    /// I/O ready
484    pub fn io_ready(&self) -> bool {
485        self.raw & (1 << 31) != 0
486    }
487}
488
489/// SDIO R5 response
490#[derive(Debug, Clone, Copy)]
491pub struct SdioRwResponse {
492    pub raw: u32,
493}
494
495impl SdioRwResponse {
496    pub fn from_raw(raw: u32) -> Self {
497        Self { raw }
498    }
499
500    /// Read/write data (bits 7:0)
501    pub fn data(&self) -> u8 {
502        (self.raw & 0xFF) as u8
503    }
504
505    /// Response flags (bits 15:8)
506    pub fn flags(&self) -> u8 {
507        ((self.raw >> 8) & 0xFF) as u8
508    }
509}
510
511/// Decode SPI R1 byte error bits (bits 1..=6 of the byte).
512///
513/// SPI R1 layout (SD spec, simplified):
514///   bit 1 = erase reset
515///   bit 2 = illegal command
516///   bit 3 = command CRC error
517///   bit 4 = erase sequence error
518///   bit 5 = address error
519///   bit 6 = parameter error
520///
521/// When multiple bits are set we return the first known error in priority
522/// order (CRC > illegal command > address > parameter > erase sequence >
523/// erase reset). If no known bit is set we preserve the raw pattern.
524fn decode_spi_card_error(bits: u8) -> CardError {
525    if bits & 0b0000_1000 != 0 {
526        CardError::CommandCrcFailed
527    } else if bits & 0b0000_0100 != 0 {
528        CardError::IllegalCommand
529    } else if bits & 0b0010_0000 != 0 {
530        CardError::AddressError
531    } else if bits & 0b0100_0000 != 0 {
532        // PARAMETER_ERROR — closest existing variant
533        CardError::AddressError
534    } else if bits & (0b0001_0000 | 0b0000_0010) != 0 {
535        // ERASE_SEQ_ERROR or ERASE_RESET — both fall under EraseSequence.
536        CardError::EraseSequence
537    } else {
538        CardError::Unknown(bits)
539    }
540}
541
542/// Decode native R1 error nibble (bits 19..=24 of the 32-bit response,
543/// passed in pre-shifted into the low 6 bits).
544///
545/// Native R1 layout:
546///   bit 19 = AKE_SEQ_ERROR (0x01 in nibble)
547///   bit 20 = CC_ERROR / controller (0x02)
548///   bit 21 = CARD_ECC_FAILED (0x04)
549///   bit 22 = ILLEGAL_COMMAND (0x08)
550///   bit 23 = COM_CRC_ERROR (0x10)
551///   bit 24 = LOCK_UNLOCK_FAILED (0x20)
552fn decode_native_card_error(bits: u8) -> CardError {
553    if bits & 0b0001_0000 != 0 {
554        CardError::CommandCrcFailed
555    } else if bits & 0b0000_1000 != 0 {
556        CardError::IllegalCommand
557    } else if bits & 0b0000_0100 != 0 {
558        CardError::CardEccFailed
559    } else if bits & 0b0000_0010 != 0 {
560        CardError::ControllerError
561    } else if bits & 0b0010_0000 != 0 {
562        // LOCK_UNLOCK_FAILED — closest existing variant
563        CardError::ControllerError
564    } else if bits & 0b0000_0001 != 0 {
565        // AKE_SEQ_ERROR — closest existing variant
566        CardError::EraseSequence
567    } else {
568        CardError::Unknown(bits)
569    }
570}
571
572#[cfg(test)]
573mod tests {
574    use super::*;
575
576    #[test]
577    fn spi_r1_idle_uses_bit_zero() {
578        let response = R1Response::from_spi_byte(0x01).unwrap();
579        assert!(response.idle());
580        assert!(!response.illegal_command());
581        assert!(response.spi_card_error().is_none());
582    }
583
584    #[test]
585    fn spi_r1_illegal_command_sets_flag_and_card_error() {
586        let response = R1Response::from_spi_byte(0x04).unwrap();
587        assert!(response.illegal_command());
588        assert_eq!(response.spi_card_error(), Some(CardError::IllegalCommand));
589    }
590
591    #[test]
592    fn spi_r1_idle_plus_illegal_command_preserves_both() {
593        let response = R1Response::from_spi_byte(0x05).unwrap();
594        assert!(response.idle());
595        assert!(response.illegal_command());
596        assert_eq!(response.spi_card_error(), Some(CardError::IllegalCommand));
597    }
598
599    #[test]
600    fn spi_r1_high_bit_is_bus_error() {
601        assert!(matches!(
602            R1Response::from_spi_byte(0x80),
603            Err(Error::BadResponse(_))
604        ));
605        assert!(matches!(
606            R1Response::from_spi_byte(0xFF),
607            Err(Error::BadResponse(_))
608        ));
609    }
610
611    #[test]
612    fn native_r1_status_bits_decoded() {
613        // status = card in transfer state (bits 12..=9 = 4)
614        let r1 = R1Response::from_native_raw(4 << 9).unwrap();
615        assert_eq!(r1.current_state(), CardState::Transfer);
616    }
617
618    #[test]
619    fn native_r1_with_illegal_command_returns_error() {
620        // illegal command = bit 22 in native R1
621        let err = R1Response::from_native_raw(1 << 22).unwrap_err();
622        assert_eq!(err, Error::CardError(CardError::IllegalCommand));
623    }
624
625    #[test]
626    fn decode_spi_card_error_priority_handles_multiple_bits() {
627        // Both illegal command (0x04) + crc failed (0x08) bits set. CRC wins.
628        assert_eq!(
629            decode_spi_card_error(0b0000_1100),
630            CardError::CommandCrcFailed
631        );
632    }
633
634    #[test]
635    fn decode_spi_card_error_unknown_for_unrecognized_bits() {
636        // bit 7 cannot occur after our mask; this exercises the fallback.
637        assert_eq!(decode_spi_card_error(0b0000_0000), CardError::Unknown(0));
638    }
639
640    #[test]
641    fn csd_v2_decodes_2gib_capacity() {
642        // CSD v2 with C_SIZE = 0x000F0F (3855) ⇒ (3855 + 1) * 1024 blocks
643        // = 3,948,544 blocks ≈ 1.88 GiB. Layout: byte 0 high bits = 0x40
644        // (CSD_STRUCTURE = 1), byte 7 low 6 bits + byte 8 + byte 9 = C_SIZE.
645        let mut raw = [0u8; 16];
646        raw[0] = 0x40;
647        raw[7] = 0x00;
648        raw[8] = 0x0F;
649        raw[9] = 0x0F;
650        let csd = CsdResponse::from_raw(raw);
651        assert_eq!(csd.version(), 1);
652        assert_eq!(csd.capacity_blocks(), Some((0x0F0F + 1) * 1024));
653    }
654
655    #[test]
656    fn csd_v1_decodes_known_capacity() {
657        // CSD v1 example: READ_BL_LEN = 9, C_SIZE = 0x0EFF, C_SIZE_MULT = 7
658        // ⇒ blocks = (0x0EFF+1) * 2^(7+2) * 2^9 / 512
659        //          = 3840 * 512 * 512 / 512 = 3840 * 512 = 1,966,080 blocks
660        let mut raw = [0u8; 16];
661        raw[0] = 0x00; // CSD v1
662        raw[5] = 0x09; // low nibble = READ_BL_LEN = 9
663        // C_SIZE = 0x0EFF stored across bytes 6 (low 2 bits) | 7 | 8 (high 2 bits)
664        // 0x0EFF = 0b0000_1110_1111_1111
665        // bits 11:10 = 00 → byte6 low 2 = 0
666        // bits 9:2  = 0b0011_1011 = 0x3B → byte7 = 0x3B
667        // bits 1:0  = 0b11 → byte8 high 2 = 0b11_xx_xxxx
668        raw[6] = 0b0000_0011; // low 2 bits = top 2 of C_SIZE = 11 → wait, recompute
669        // Actually: C_SIZE bits 11:10 → byte6[1:0]; bits 9:2 → byte7[7:0]; bits 1:0 → byte8[7:6]
670        // For C_SIZE = 0x0EFF = 0b1110_1111_1111:
671        //   bits 11:10 = 11
672        //   bits 9:2  = 0b1011_1111 = 0xBF
673        //   bits 1:0  = 0b11
674        raw[6] = 0b0000_0011;
675        raw[7] = 0xBF;
676        raw[8] = 0b1100_0000;
677        // C_SIZE_MULT = 7 = 0b111 stored in byte9[1:0] (top 2 bits of MULT)
678        // and byte10[7] (low bit of MULT)
679        raw[9] = 0b0000_0011;
680        raw[10] = 0b1000_0000;
681        let csd = CsdResponse::from_raw(raw);
682        assert_eq!(csd.version(), 0);
683        let expected = (0x0EFFu64 + 1) * (1 << (7 + 2)) * (1 << 9) / 512;
684        assert_eq!(csd.capacity_blocks(), Some(expected));
685    }
686
687    #[test]
688    fn csd_unknown_version_returns_none() {
689        let mut raw = [0u8; 16];
690        raw[0] = 0x80; // CSD_STRUCTURE = 2 (SDUC v3) — not yet supported
691        let csd = CsdResponse::from_raw(raw);
692        assert_eq!(csd.version(), 2);
693        assert_eq!(csd.capacity_blocks(), None);
694    }
695
696    #[test]
697    fn cid_decodes_manufacturer_oem_product_serial_and_date() {
698        // Hand-rolled CID: MID=0x03, OID="SD", PNM="ABC12", PRV=2.7,
699        //   PSN=0xDEAD_BEEF, MDT year=2026 (offset 26 = 0x1A) month=5.
700        let mut raw = [0u8; 16];
701        raw[0] = 0x03;
702        raw[1] = b'S';
703        raw[2] = b'D';
704        raw[3] = b'A';
705        raw[4] = b'B';
706        raw[5] = b'C';
707        raw[6] = b'1';
708        raw[7] = b'2';
709        raw[8] = (2 << 4) | 7;
710        raw[9] = 0xDE;
711        raw[10] = 0xAD;
712        raw[11] = 0xBE;
713        raw[12] = 0xEF;
714        // MDT bits 19:8 = year[7:0] (8 bits) + month[3:0] (4 bits)
715        // year = 0x1A = 0001 1010: high nibble in raw[13][3:0], low nibble in raw[14][7:4]
716        raw[13] = 0x01; // year high nibble = 1
717        raw[14] = 0xA5; // year low nibble = A, month nibble = 5
718
719        let cid = CidResponse::from_raw(raw);
720        assert_eq!(cid.manufacturer_id(), 0x03);
721        assert_eq!(&cid.oem_id(), b"SD");
722        assert_eq!(&cid.product_name(), b"ABC12");
723        assert_eq!(cid.product_revision(), (2, 7));
724        assert_eq!(cid.serial_number(), 0xDEAD_BEEF);
725        assert_eq!(cid.manufacture_date(), (2026, 5));
726    }
727
728    #[test]
729    fn switch_status_reports_high_speed_when_group_one_function_one() {
730        let mut raw = [0u8; 64];
731        raw[16] = 0x01; // group 2 = 0, group 1 = 1 (high speed)
732        let status = SwitchStatus::from_raw(raw);
733        assert_eq!(status.selected_function(1), 1);
734        assert!(status.high_speed_active());
735    }
736
737    #[test]
738    fn switch_status_reports_access_mode_support_bits() {
739        let mut raw = [0u8; 64];
740        raw[13] = (1 << 1) | (1 << 3);
741        let status = SwitchStatus::from_raw(raw);
742
743        assert!(status.access_mode_supported(1));
744        assert!(status.access_mode_supported(3));
745        assert!(!status.access_mode_supported(2));
746        assert!(!status.access_mode_supported(8));
747    }
748
749    #[test]
750    fn switch_status_reports_default_when_group_one_function_zero() {
751        let raw = [0u8; 64];
752        let status = SwitchStatus::from_raw(raw);
753        assert_eq!(status.selected_function(1), 0);
754        assert!(!status.high_speed_active());
755    }
756
757    #[test]
758    fn switch_status_unsupported_group_returns_0xf() {
759        let mut raw = [0u8; 64];
760        raw[16] = 0xF0; // group 2 unsupported, group 1 = 0
761        let status = SwitchStatus::from_raw(raw);
762        assert_eq!(status.selected_function(2), 0xF);
763        assert_eq!(status.selected_function(7), 0xF); // out of range
764    }
765}