1//! SD Card Registers
2//!
3//! Register representations can be created from an array of little endian
4//! words. Note that the SDMMC protocol transfers the registers in big endian
5//! byte order.
6//!
7//! ```
8//! # use sdio_host::SCR;
9//! let scr: SCR = [0, 1].into();
10//! ```
11//!
12//! ## Reference documents:
13//!
14//! PLSS_v7_10: Physical Layer Specification Simplified Specification Version
15//! 7.10. March 25, 2020. (C) SD Card Association
16
17#![no_std]
18
19use core::{fmt, str};
20
21pub mod cmd;
22#[doc(inline)]
23pub use cmd::Cmd;
24
25/// Types of SD Card
26#[derive(Debug, Copy, Clone)]
27#[non_exhaustive]
28pub enum CardCapacity {
29 /// Standard Capacity (< 2Gb)
30 SDSC,
31 /// High capacity (< 32Gb)
32 SDHC,
33}
34impl Default for CardCapacity {
35 fn default() -> Self {
36 CardCapacity::SDSC
37 }
38}
39
40#[non_exhaustive]
41#[derive(Debug, Copy, Clone, Eq, PartialEq)]
42pub enum SDSpecVersion {
43 /// Version 1.0 and and 1.0.1
44 V1_0,
45 /// Version 1.10
46 V1_10,
47 /// Version 2.0
48 V2,
49 /// Version 3.0
50 V3,
51 /// Version 4.0
52 V4,
53 /// Version 5.0
54 V5,
55 /// Version 6.0
56 V6,
57 /// Version 7.0
58 V7,
59 /// Version not known by this crate
60 Unknown,
61}
62
63/// The number of data lines in use on the SDMMC bus
64#[derive(Copy, Clone, Debug, Eq, PartialEq)]
65#[allow(missing_docs)]
66pub enum BusWidth {
67 #[non_exhaustive]
68 Unknown,
69 One = 1,
70 Four = 4,
71 Eight = 8,
72}
73
74#[derive(Debug, Copy, Clone, Eq, PartialEq)]
75pub enum BlockSize {
76 #[non_exhaustive]
77 Unknown = 0,
78 B512 = 9,
79 B1024 = 10,
80 B2048 = 11,
81}
82
83#[derive(Copy, Clone, Eq, PartialEq)]
84#[allow(non_camel_case_types)]
85pub enum CurrentConsumption {
86 I_0mA,
87 I_1mA,
88 I_5mA,
89 I_10mA,
90 I_25mA,
91 I_35mA,
92 I_45mA,
93 I_60mA,
94 I_80mA,
95 I_100mA,
96 I_200mA,
97}
98impl From<&CurrentConsumption> for u32 {
99 fn from(i: &CurrentConsumption) -> u32 {
100 match i {
101 CurrentConsumption::I_0mA => 0,
102 CurrentConsumption::I_1mA => 1,
103 CurrentConsumption::I_5mA => 5,
104 CurrentConsumption::I_10mA => 10,
105 CurrentConsumption::I_25mA => 25,
106 CurrentConsumption::I_35mA => 35,
107 CurrentConsumption::I_45mA => 45,
108 CurrentConsumption::I_60mA => 60,
109 CurrentConsumption::I_80mA => 80,
110 CurrentConsumption::I_100mA => 100,
111 CurrentConsumption::I_200mA => 200,
112 }
113 }
114}
115impl CurrentConsumption {
116 fn from_minimum_reg(reg: u128) -> CurrentConsumption {
117 match reg {
118 0 => CurrentConsumption::I_0mA,
119 1 => CurrentConsumption::I_1mA,
120 2 => CurrentConsumption::I_5mA,
121 3 => CurrentConsumption::I_10mA,
122 4 => CurrentConsumption::I_25mA,
123 5 => CurrentConsumption::I_35mA,
124 6 => CurrentConsumption::I_60mA,
125 _ => CurrentConsumption::I_100mA,
126 }
127 }
128 fn from_maximum_reg(reg: u128) -> CurrentConsumption {
129 match reg {
130 0 => CurrentConsumption::I_0mA,
131 1 => CurrentConsumption::I_5mA,
132 2 => CurrentConsumption::I_10mA,
133 3 => CurrentConsumption::I_25mA,
134 4 => CurrentConsumption::I_35mA,
135 5 => CurrentConsumption::I_45mA,
136 6 => CurrentConsumption::I_80mA,
137 _ => CurrentConsumption::I_200mA,
138 }
139 }
140}
141impl fmt::Debug for CurrentConsumption {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 let ma: u32 = self.into();
144 write!(f, "{} mA", ma)
145 }
146}
147
148/// CURRENT_STATE enum. Used for R1 response in command queue mode.
149///
150/// Ref PLSS_v7_10 Table 4-75
151#[derive(Eq, PartialEq, Copy, Clone, Debug)]
152#[allow(dead_code)]
153pub enum CurrentState {
154 /// Card state is ready
155 Ready = 1,
156 /// Card is in identification state
157 Identification = 2,
158 /// Card is in standby state
159 Standby = 3,
160 /// Card is in transfer state
161 Transfer = 4,
162 /// Card is sending an operation
163 Sending = 5,
164 /// Card is receiving operation information
165 Receiving = 6,
166 /// Card is in programming state
167 Programming = 7,
168 /// Card is disconnected
169 Disconnected = 8,
170 // 9 - 14: Reserved
171 // 15: Resevered
172 /// Error
173 Error = 128,
174}
175
176impl From<u8> for CurrentState {
177 fn from(n: u8) -> Self {
178 match n {
179 1 => Self::Ready,
180 2 => Self::Identification,
181 3 => Self::Standby,
182 4 => Self::Transfer,
183 5 => Self::Sending,
184 6 => Self::Receiving,
185 7 => Self::Programming,
186 8 => Self::Disconnected,
187 _ => Self::Error,
188 }
189 }
190}
191
192/// Operation Conditions Register (OCR)
193///
194/// R3
195#[derive(Clone, Copy, Default)]
196pub struct OCR(u32);
197impl From<u32> for OCR {
198 fn from(word: u32) -> Self {
199 Self(word)
200 }
201}
202impl OCR {
203 /// VDD voltage window
204 pub fn voltage_window_mv(&self) -> Option<(u16, u16)> {
205 let mut window = (self.0 >> 15) & 0x1FF;
206 let mut min = 2_700;
207
208 while window & 1 == 0 && window != 0 {
209 min += 100;
210 window >>= 1;
211 }
212 let mut max = min;
213 while window != 0 {
214 max += 100;
215 window >>= 1;
216 }
217
218 if max == min {
219 None
220 } else {
221 Some((min, max))
222 }
223 }
224 /// Switching to 1.8V Accepted (S18A). Only UHS-I cards support this bit
225 pub fn v18_allowed(&self) -> bool {
226 self.0 & 0x0100_0000 != 0
227 }
228 /// Over 2TB support Status. Only SDUC card support this bit
229 pub fn over_2tb(&self) -> bool {
230 self.0 & 0x0800_0000 != 0
231 }
232 /// Indicates whether the card supports UHS-II Interface
233 pub fn uhs2_card_status(&self) -> bool {
234 self.0 & 0x2000_0000 != 0
235 }
236 /// Card Capacity Status (CCS). True for SDHC/SDXC/SDUC, false for SDSC
237 pub fn high_capacity(&self) -> bool {
238 self.0 & 0x4000_0000 != 0
239 }
240 /// Card power up status bit (busy)
241 pub fn is_busy(&self) -> bool {
242 self.0 & 0x8000_0000 == 0 // Set active LOW
243 }
244}
245impl fmt::Debug for OCR {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 f&mut DebugStruct<'_, '_>.debug_struct("OCR: Operation Conditions Register")
248 .field(
249 "Voltage Window (mV)",
250 &self.voltage_window_mv().unwrap_or((0, 0)),
251 )
252 .field("S18A (UHS-I only)", &self.v18_allowed())
253 .field("Over 2TB flag (SDUC only)", &self.over_2tb())
254 .field("UHS-II Card", &self.uhs2_card_status())
255 .field(
256 "Card Capacity Status (CSS)",
257 &if self.high_capacity() {
258 "SDHC/SDXC/SDUC"
259 } else {
260 "SDSC"
261 },
262 )
263 .field(name:"Busy", &self.is_busy())
264 .finish()
265 }
266}
267
268/// Card Identification Register (CID)
269///
270/// R2
271#[derive(Clone, Copy, Default)]
272pub struct CID {
273 inner: u128,
274 bytes: [u8; 16],
275}
276impl From<u128> for CID {
277 fn from(inner: u128) -> Self {
278 Self {
279 inner,
280 bytes: inner.to_be_bytes(),
281 }
282 }
283}
284/// From little endian words
285impl From<[u32; 4]> for CID {
286 fn from(words: [u32; 4]) -> Self {
287 let inner: u128 = ((words[3] as u128) << 96)
288 | ((words[2] as u128) << 64)
289 | ((words[1] as u128) << 32)
290 | words[0] as u128;
291 inner.into()
292 }
293}
294impl CID {
295 /// Manufacturer ID
296 pub fn manufacturer_id(&self) -> u8 {
297 self.bytes[0]
298 }
299 /// OEM/Application ID
300 pub fn oem_id(&self) -> &str {
301 str::from_utf8(&self.bytes[1..3]).unwrap_or(&"<ERR>")
302 }
303 /// Product name
304 pub fn product_name(&self) -> &str {
305 str::from_utf8(&self.bytes[3..8]).unwrap_or(&"<ERR>")
306 }
307 /// Product revision
308 pub fn product_revision(&self) -> u8 {
309 self.bytes[8]
310 }
311 /// Product serial number
312 pub fn serial(&self) -> u32 {
313 (self.inner >> 24) as u32
314 }
315 /// Manufacturing date
316 pub fn manufacturing_date(&self) -> (u8, u16) {
317 (
318 (self.inner >> 8) as u8 & 0xF, // Month
319 ((self.inner >> 12) as u16 & 0xFF) + 2000, // Year
320 )
321 }
322 #[allow(unused)]
323 fn crc7(&self) -> u8 {
324 (self.bytes[15] >> 1) & 0x7F
325 }
326}
327impl fmt::Debug for CID {
328 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329 f&mut DebugStruct<'_, '_>.debug_struct("CID: Card Identification")
330 .field("Manufacturer ID", &self.manufacturer_id())
331 .field("OEM ID", &self.oem_id())
332 .field("Product Name", &self.product_name())
333 .field("Product Revision", &self.product_revision())
334 .field("Product Serial Number", &self.serial())
335 .field(name:"Manufacturing Date", &self.manufacturing_date())
336 .finish()
337 }
338}
339
340/// SD CARD Configuration Register (SCR)
341#[derive(Clone, Copy, Default)]
342pub struct SCR(pub u64);
343/// From little endian words
344impl From<[u32; 2]> for SCR {
345 fn from(words: [u32; 2]) -> Self {
346 Self(((words[1] as u64) << 32) | words[0] as u64)
347 }
348}
349impl SCR {
350 /// Physical Layer Specification Version Number
351 pub fn version(&self) -> SDSpecVersion {
352 let spec = (self.0 >> 56) & 0xF;
353 let spec3 = (self.0 >> 47) & 1;
354 let spec4 = (self.0 >> 42) & 1;
355 let specx = (self.0 >> 38) & 0xF;
356
357 // Ref PLSS_v7_10 Table 5-17
358 match (spec, spec3, spec4, specx) {
359 (0, 0, 0, 0) => SDSpecVersion::V1_0,
360 (1, 0, 0, 0) => SDSpecVersion::V1_10,
361 (2, 0, 0, 0) => SDSpecVersion::V2,
362 (2, 1, 0, 0) => SDSpecVersion::V3,
363 (2, 1, 1, 0) => SDSpecVersion::V4,
364 (2, 1, _, 1) => SDSpecVersion::V5,
365 (2, 1, _, 2) => SDSpecVersion::V6,
366 (2, 1, _, 3) => SDSpecVersion::V7,
367 _ => SDSpecVersion::Unknown,
368 }
369 }
370 /// Bus widths supported
371 pub fn bus_widths(&self) -> u8 {
372 // Ref PLSS_v7_10 Table 5-21
373 ((self.0 >> 48) as u8) & 0xF
374 }
375 /// Supports 1-bit bus width
376 pub fn bus_width_one(&self) -> bool {
377 (self.0 >> 48) & 1 != 0
378 }
379 /// Supports 4-bit bus width
380 pub fn bus_width_four(&self) -> bool {
381 (self.0 >> 50) & 1 != 0
382 }
383}
384impl fmt::Debug for SCR {
385 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386 f&mut DebugStruct<'_, '_>.debug_struct("SCR: SD CARD Configuration Register")
387 .field("Version", &self.version())
388 .field("1-bit width", &self.bus_width_one())
389 .field(name:"4-bit width", &self.bus_width_four())
390 .finish()
391 }
392}
393
394/// Card Specific Data (CSD)
395#[derive(Clone, Copy, Default)]
396pub struct CSD(u128);
397impl From<u128> for CSD {
398 fn from(inner: u128) -> Self {
399 Self(inner)
400 }
401}
402/// From little endian words
403impl From<[u32; 4]> for CSD {
404 fn from(words: [u32; 4]) -> Self {
405 let inner: u128 = ((words[3] as u128) << 96)
406 | ((words[2] as u128) << 64)
407 | ((words[1] as u128) << 32)
408 | words[0] as u128;
409 inner.into()
410 }
411}
412impl CSD {
413 /// CSD structure version
414 pub fn version(&self) -> u8 {
415 (self.0 >> 126) as u8 & 3
416 }
417 /// Maximum data transfer rate per one data line
418 pub fn transfer_rate(&self) -> u8 {
419 (self.0 >> 96) as u8
420 }
421 /// Maximum block length. In an SD Memory Card the WRITE_BL_LEN is
422 /// always equal to READ_BL_LEN
423 pub fn block_length(&self) -> BlockSize {
424 // Read block length
425 match (self.0 >> 80) & 0xF {
426 9 => BlockSize::B512,
427 10 => BlockSize::B1024,
428 11 => BlockSize::B2048,
429 _ => BlockSize::Unknown,
430 }
431 }
432 /// Number of blocks in the card
433 pub fn block_count(&self) -> u32 {
434 match self.version() {
435 0 => {
436 // SDSC
437 let c_size: u16 = ((self.0 >> 62) as u16) & 0xFFF;
438 let c_size_mult: u8 = ((self.0 >> 47) as u8) & 7;
439
440 ((c_size + 1) as u32) * ((1 << (c_size_mult + 2)) as u32)
441 }
442 1 => {
443 // SDHC / SDXC
444 (((self.0 >> 48) as u32 & 0x3F_FFFF) + 1) * 1024
445 }
446 2 => {
447 // SDUC
448 (((self.0 >> 48) as u32 & 0xFFF_FFFF) + 1) * 1024
449 }
450 _ => 0,
451 }
452 }
453 /// Card size in bytes
454 pub fn card_size(&self) -> u64 {
455 let block_size_bytes = 1 << self.block_length() as u64;
456
457 (self.block_count() as u64) * block_size_bytes
458 }
459 /// Maximum read current at the minimum VDD
460 pub fn read_current_minimum_vdd(&self) -> CurrentConsumption {
461 CurrentConsumption::from_minimum_reg(self.0 >> 59)
462 }
463 /// Maximum write current at the minimum VDD
464 pub fn write_current_minimum_vdd(&self) -> CurrentConsumption {
465 CurrentConsumption::from_minimum_reg(self.0 >> 56)
466 }
467 /// Maximum read current at the maximum VDD
468 pub fn read_current_maximum_vdd(&self) -> CurrentConsumption {
469 CurrentConsumption::from_maximum_reg(self.0 >> 53)
470 }
471 /// Maximum write current at the maximum VDD
472 pub fn write_current_maximum_vdd(&self) -> CurrentConsumption {
473 CurrentConsumption::from_maximum_reg(self.0 >> 50)
474 }
475 /// Erase size (in blocks)
476 pub fn erase_size_blocks(&self) -> u32 {
477 if (self.0 >> 46) & 1 == 1 {
478 // ERASE_BLK_EN
479 1
480 } else {
481 let sector_size_tens = (self.0 >> 43) & 0x7;
482 let sector_size_units = (self.0 >> 39) & 0xF;
483
484 (sector_size_tens as u32 * 10) + (sector_size_units as u32)
485 }
486 }
487}
488impl fmt::Debug for CSD {
489 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490 f&mut DebugStruct<'_, '_>.debug_struct("CSD: Card Specific Data")
491 .field("Transfer Rate", &self.transfer_rate())
492 .field("Block Count", &self.block_count())
493 .field("Card Size (bytes)", &self.card_size())
494 .field("Read I (@min VDD)", &self.read_current_minimum_vdd())
495 .field("Write I (@min VDD)", &self.write_current_minimum_vdd())
496 .field("Read I (@max VDD)", &self.read_current_maximum_vdd())
497 .field("Write I (@max VDD)", &self.write_current_maximum_vdd())
498 .field(name:"Erase Size (Blocks)", &self.erase_size_blocks())
499 .finish()
500 }
501}
502
503/// Card Status (R1)
504///
505/// Error and state information of an executed command
506///
507/// Ref PLSS_v7_10 Section 4.10.1
508#[derive(Clone, Copy)]
509pub struct CardStatus(u32);
510
511impl From<u32> for CardStatus {
512 fn from(word: u32) -> Self {
513 Self(word)
514 }
515}
516
517impl CardStatus {
518 /// Command's argument was out of range
519 pub fn out_of_range(&self) -> bool {
520 self.0 & 0x8000_0000 != 0
521 }
522 /// Misaligned address
523 pub fn address_error(&self) -> bool {
524 self.0 & 0x4000_0000 != 0
525 }
526 /// Block len error
527 pub fn block_len_error(&self) -> bool {
528 self.0 & 0x2000_0000 != 0
529 }
530 /// Error in the erase commands sequence
531 pub fn erase_seq_error(&self) -> bool {
532 self.0 & 0x1000_0000 != 0
533 }
534 /// Invalid selection of blocks for erase
535 pub fn erase_param(&self) -> bool {
536 self.0 & 0x800_0000 != 0
537 }
538 /// Host attempted to write to protected area
539 pub fn wp_violation(&self) -> bool {
540 self.0 & 0x400_0000 != 0
541 }
542 /// Card is locked by the host
543 pub fn card_is_locked(&self) -> bool {
544 self.0 & 0x200_0000 != 0
545 }
546 /// Password error
547 pub fn lock_unlock_failed(&self) -> bool {
548 self.0 & 0x100_0000 != 0
549 }
550 /// Crc check of previous command failed
551 pub fn com_crc_error(&self) -> bool {
552 self.0 & 0x80_0000 != 0
553 }
554 /// Command is not legal for the card state
555 pub fn illegal_command(&self) -> bool {
556 self.0 & 0x40_0000 != 0
557 }
558 /// Card internal ECC failed
559 pub fn card_ecc_failed(&self) -> bool {
560 self.0 & 0x20_0000 != 0
561 }
562 /// Internal controller error
563 pub fn cc_error(&self) -> bool {
564 self.0 & 0x10_0000 != 0
565 }
566 /// A General error occurred
567 pub fn error(&self) -> bool {
568 self.0 & 0x8_0000 != 0
569 }
570 // Bit 18: Reserved
571 // Bit 17: Reserved
572 /// CSD error
573 pub fn csd_overwrite(&self) -> bool {
574 self.0 & 0x1_0000 != 0
575 }
576 /// Some blocks where skipped while erasing
577 pub fn wp_erase_skip(&self) -> bool {
578 self.0 & 0x8000 != 0
579 }
580 /// Command was executed without internal ECC
581 pub fn ecc_disabled(&self) -> bool {
582 self.0 & 0x4000 != 0
583 }
584 /// Erase sequence was aborted
585 pub fn erase_reset(&self) -> bool {
586 self.0 & 0x2000 != 0
587 }
588 /// Current card state
589 pub fn state(&self) -> CurrentState {
590 CurrentState::from(((self.0 >> 9) & 0xF) as u8)
591 }
592 /// Corresponds to buffer empty signaling on the bus
593 pub fn ready_for_data(&self) -> bool {
594 self.0 & 0x100 != 0
595 }
596 // Bit 7: Reserved
597 /// Extension function specific status
598 pub fn fx_event(&self) -> bool {
599 self.0 & 0x40 != 0
600 }
601 /// The card will accept a ACMD
602 pub fn app_cmd(&self) -> bool {
603 self.0 & 0x20 != 0
604 }
605 // Bit 4: Reserved
606 /// Authentication sequence error
607 pub fn ake_seq_error(&self) -> bool {
608 self.0 & 0x8 != 0
609 }
610 // Bits 2,1,0: Reserved
611}
612
613impl fmt::Debug for CardStatus {
614 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615 f.debug_struct("Card Status")
616 .field("Out of range error", &self.out_of_range())
617 .field("Address error", &self.address_error())
618 .field("Block len error", &self.block_len_error())
619 .field("Erase seq error", &self.erase_seq_error())
620 .field("Erase param error", &self.erase_param())
621 .field("Write protect error", &self.wp_violation())
622 .field("Card locked", &self.card_is_locked())
623 .field("Password lock unlock error", &self.lock_unlock_failed())
624 .field(
625 "Crc check for the previous command failed",
626 &self.com_crc_error(),
627 )
628 .field("Illegal command", &self.illegal_command())
629 .field("Card internal ecc failed", &self.card_ecc_failed())
630 .field("Internal card controller error", &self.cc_error())
631 .field("General Error", &self.error())
632 .field("Csd error", &self.csd_overwrite())
633 .field("Write protect error", &self.wp_erase_skip())
634 .field("Command ecc disabled", &self.ecc_disabled())
635 .field("Erase sequence cleared", &self.erase_reset())
636 .field("Card state", &self.state())
637 .field("Buffer empty", &self.ready_for_data())
638 .field("Extension event", &self.fx_event())
639 .field("Card expects app cmd", &self.app_cmd())
640 .field("Auth process error", &self.ake_seq_error())
641 .finish()
642 }
643}
644
645/// SD Status
646///
647/// Status bits related to SD Memory Card proprietary features
648///
649/// Ref PLSS_v7_10 Section 4.10.2 SD Status
650#[derive(Clone, Copy, Default)]
651pub struct SDStatus {
652 inner: [u32; 16],
653}
654/// From little endian words
655impl From<[u32; 16]> for SDStatus {
656 fn from(inner: [u32; 16]) -> Self {
657 Self { inner }
658 }
659}
660impl SDStatus {
661 /// Current data bus width
662 pub fn bus_width(&self) -> BusWidth {
663 match (self.inner[15] >> 30) & 3 {
664 0 => BusWidth::One,
665 2 => BusWidth::Four,
666 _ => BusWidth::Unknown,
667 }
668 }
669 /// Is the card currently in the secured mode
670 pub fn secure_mode(&self) -> bool {
671 self.inner[15] & 0x2000_0000 != 0
672 }
673 /// SD Memory Card type (ROM, OTP, etc)
674 pub fn sd_memory_card_type(&self) -> u16 {
675 self.inner[15] as u16
676 }
677 /// SDHC / SDXC: Capacity of Protected Area in bytes
678 pub fn protected_area_size(&self) -> u32 {
679 self.inner[14]
680 }
681 /// Speed Class
682 pub fn speed_class(&self) -> u8 {
683 (self.inner[13] >> 24) as u8
684 }
685 /// "Performance Move" indicator in 1 MB/s units
686 pub fn move_performance(&self) -> u8 {
687 (self.inner[13] >> 16) as u8
688 }
689 /// Allocation Unit (AU) size. Lookup in PLSS v7_10 Table 4-47
690 pub fn allocation_unit_size(&self) -> u8 {
691 (self.inner[13] >> 12) as u8 & 0xF
692 }
693 /// Indicates N_Erase, in units of AU
694 pub fn erase_size(&self) -> u16 {
695 (self.inner[13] & 0xFF) as u16 | ((self.inner[12] >> 24) & 0xFF) as u16
696 }
697 /// Indicates T_Erase / Erase Timeout (s)
698 pub fn erase_timeout(&self) -> u8 {
699 (self.inner[12] >> 18) as u8 & 0x3F
700 }
701 /// Video speed class
702 pub fn video_speed_class(&self) -> u8 {
703 (self.inner[11] & 0xFF) as u8
704 }
705 /// Application Performance Class
706 pub fn app_perf_class(&self) -> u8 {
707 (self.inner[9] >> 16) as u8 & 0xF
708 }
709 /// Discard Support
710 pub fn discard_support(&self) -> bool {
711 self.inner[8] & 0x0200_0000 != 0
712 }
713}
714impl fmt::Debug for SDStatus {
715 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
716 f&mut DebugStruct<'_, '_>.debug_struct("SD Status")
717 .field("Bus Width", &self.bus_width())
718 .field("Secured Mode", &self.secure_mode())
719 .field("SD Memory Card Type", &self.sd_memory_card_type())
720 .field("Protected Area Size (B)", &self.protected_area_size())
721 .field("Speed Class", &self.speed_class())
722 .field("Video Speed Class", &self.video_speed_class())
723 .field("Application Performance Class", &self.app_perf_class())
724 .field("Move Performance (MB/s)", &self.move_performance())
725 .field("AU Size", &self.allocation_unit_size())
726 .field("Erase Size (units of AU)", &self.erase_size())
727 .field("Erase Timeout (s)", &self.erase_timeout())
728 .field(name:"Discard Support", &self.discard_support())
729 .finish()
730 }
731}
732
733/// Relative Card Address (RCA)
734///
735/// R6
736#[derive(Copy, Clone, Default)]
737pub struct RCA(u32);
738impl From<u32> for RCA {
739 fn from(word: u32) -> Self {
740 Self(word)
741 }
742}
743impl RCA {
744 /// Address of card
745 pub fn address(&self) -> u16 {
746 (self.0 >> 16) as u16
747 }
748 /// Status
749 pub fn status(&self) -> u16 {
750 self.0 as u16
751 }
752}
753
754/// Card interface condition (R7)
755#[derive(Copy, Clone, Default)]
756pub struct CIC(u32);
757impl From<u32> for CIC {
758 fn from(word: u32) -> Self {
759 Self(word)
760 }
761}
762impl CIC {
763 /// The voltage range the card accepts
764 pub fn voltage_accepted(&self) -> u8 {
765 (self.0 >> 8) as u8
766 }
767 /// Echo-back check pattern
768 pub fn pattern(&self) -> u8 {
769 self.0 as u8
770 }
771}
772