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