sdmmc_protocol/
ext_csd.rs1use crate::cmd::ext_csd;
10
11#[derive(Debug, Clone)]
18pub struct ExtCsd {
19 raw: [u8; 512],
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct DeviceType {
25 pub raw: u8,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum MmcBusWidth {
31 Sdr1,
33 Sdr4,
35 Sdr8,
37 Ddr4,
39 Ddr8,
41 Unknown(u8),
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum MmcTiming {
48 Compat,
50 HighSpeed,
52 Hs200,
54 Hs400,
56 Unknown(u8),
58}
59
60impl ExtCsd {
61 pub fn from_bytes(raw: [u8; 512]) -> Self {
62 Self { raw }
63 }
64
65 pub fn as_bytes(&self) -> &[u8; 512] {
68 &self.raw
69 }
70
71 pub fn sector_count(&self) -> Option<u32> {
75 let s = ext_csd::SEC_COUNT;
76 let v = u32::from_le_bytes([
77 self.raw[s],
78 self.raw[s + 1],
79 self.raw[s + 2],
80 self.raw[s + 3],
81 ]);
82 if v == 0 { None } else { Some(v) }
83 }
84
85 pub fn device_type(&self) -> DeviceType {
86 DeviceType {
87 raw: self.raw[ext_csd::DEVICE_TYPE],
88 }
89 }
90
91 pub fn bus_width(&self) -> MmcBusWidth {
92 match self.raw[ext_csd::BUS_WIDTH] {
93 0 => MmcBusWidth::Sdr1,
94 1 => MmcBusWidth::Sdr4,
95 2 => MmcBusWidth::Sdr8,
96 5 => MmcBusWidth::Ddr4,
97 6 => MmcBusWidth::Ddr8,
98 other => MmcBusWidth::Unknown(other),
99 }
100 }
101
102 pub fn timing(&self) -> MmcTiming {
103 match self.raw[ext_csd::HS_TIMING] & 0x0F {
104 0 => MmcTiming::Compat,
105 1 => MmcTiming::HighSpeed,
106 2 => MmcTiming::Hs200,
107 3 => MmcTiming::Hs400,
108 other => MmcTiming::Unknown(other),
109 }
110 }
111}
112
113impl DeviceType {
114 pub fn supports_hs_52(&self) -> bool {
115 self.raw & ext_csd::device_type::HS_52 != 0
116 }
117 pub fn supports_hs_26(&self) -> bool {
118 self.raw & ext_csd::device_type::HS_26 != 0
119 }
120 pub fn supports_hs200_18v(&self) -> bool {
121 self.raw & ext_csd::device_type::HS200_18V != 0
122 }
123 pub fn supports_hs200_12v(&self) -> bool {
124 self.raw & ext_csd::device_type::HS200_12V != 0
125 }
126 pub fn supports_hs200(&self) -> bool {
127 self.supports_hs200_18v() || self.supports_hs200_12v()
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 fn ext_csd_with(field: usize, val: &[u8]) -> ExtCsd {
136 let mut raw = [0u8; 512];
137 raw[field..field + val.len()].copy_from_slice(val);
138 ExtCsd::from_bytes(raw)
139 }
140
141 #[test]
142 fn sector_count_from_ext_csd() {
143 let e = ext_csd_with(ext_csd::SEC_COUNT, &[0x00, 0x00, 0x80, 0x00]);
145 assert_eq!(e.sector_count(), Some(0x0080_0000));
146 }
147
148 #[test]
149 fn sector_count_zero_means_use_csd() {
150 let e = ExtCsd::from_bytes([0u8; 512]);
151 assert_eq!(e.sector_count(), None);
152 }
153
154 #[test]
155 fn device_type_decodes_known_bits() {
156 let e = ext_csd_with(ext_csd::DEVICE_TYPE, &[0b0011_0011]);
157 let dt = e.device_type();
158 assert!(dt.supports_hs_26());
159 assert!(dt.supports_hs_52());
160 assert!(dt.supports_hs200_18v());
161 assert!(dt.supports_hs200_12v());
162 assert!(dt.supports_hs200());
163 }
164
165 #[test]
166 fn bus_width_and_timing_round_trip() {
167 let mut raw = [0u8; 512];
168 raw[ext_csd::BUS_WIDTH] = 2; raw[ext_csd::HS_TIMING] = 2; let e = ExtCsd::from_bytes(raw);
171 assert_eq!(e.bus_width(), MmcBusWidth::Sdr8);
172 assert_eq!(e.timing(), MmcTiming::Hs200);
173 }
174}