1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
//! SD card register representations

use core::fmt;
use core::str;

#[non_exhaustive]
#[derive(Debug, Copy, Clone)]
pub enum CardVersion {
    V1_0,
    V1_1,
    V2,
    V3,
    V4,
    V5,
    V6,
    V7,
    Unknown,
}

#[derive(Debug, Copy, Clone)]
pub enum BlockSize {
    B512 = 9,
    B1024 = 10,
    B2048 = 11,
}

#[derive(Copy, Clone)]
#[allow(non_camel_case_types)]
pub enum CurrentConsumption {
    I_0mA,
    I_1mA,
    I_5mA,
    I_10mA,
    I_25mA,
    I_35mA,
    I_45mA,
    I_60mA,
    I_80mA,
    I_100mA,
    I_200mA,
}
impl From<&CurrentConsumption> for u32 {
    fn from(i: &CurrentConsumption) -> u32 {
        match i {
            CurrentConsumption::I_0mA => 0,
            CurrentConsumption::I_1mA => 1,
            CurrentConsumption::I_5mA => 5,
            CurrentConsumption::I_10mA => 10,
            CurrentConsumption::I_25mA => 25,
            CurrentConsumption::I_35mA => 35,
            CurrentConsumption::I_45mA => 45,
            CurrentConsumption::I_60mA => 60,
            CurrentConsumption::I_80mA => 80,
            CurrentConsumption::I_100mA => 100,
            CurrentConsumption::I_200mA => 200,
        }
    }
}
impl CurrentConsumption {
    fn from_minimum_reg(reg: u128) -> CurrentConsumption {
        match reg {
            0 => CurrentConsumption::I_0mA,
            1 => CurrentConsumption::I_1mA,
            2 => CurrentConsumption::I_5mA,
            3 => CurrentConsumption::I_10mA,
            4 => CurrentConsumption::I_25mA,
            5 => CurrentConsumption::I_35mA,
            6 => CurrentConsumption::I_60mA,
            _ => CurrentConsumption::I_100mA,
        }
    }
    fn from_maximum_reg(reg: u128) -> CurrentConsumption {
        match reg {
            0 => CurrentConsumption::I_0mA,
            1 => CurrentConsumption::I_5mA,
            2 => CurrentConsumption::I_10mA,
            3 => CurrentConsumption::I_25mA,
            4 => CurrentConsumption::I_35mA,
            5 => CurrentConsumption::I_45mA,
            6 => CurrentConsumption::I_80mA,
            _ => CurrentConsumption::I_200mA,
        }
    }
}
impl fmt::Debug for CurrentConsumption {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let ma: u32 = self.into();
        write!(f, "{} mA", ma)
    }
}

/// Operation Conditions Register (OCR)
#[derive(Clone, Copy, Default)]
pub struct OCR(pub u32);
impl OCR {
    /// VDD voltage window
    pub fn voltage_window_mv(&self) -> Option<(u16, u16)> {
        let mut window = (self.0 >> 15) & 0x1FF;
        let mut min = 2_700;

        while window & 1 == 0 && window != 0 {
            min += 100;
            window >>= 1;
        }
        let mut max = min;
        while window != 0 {
            max += 100;
            window >>= 1;
        }

        if max == min {
            None
        } else {
            Some((min, max))
        }
    }
    /// Switching to 1.8V Accepted (S18A). Only UHS-I cards support this bit
    pub fn s18a(&self) -> bool {
        self.0 & 0x0100_0000 != 0
    }
    /// Indicates whether the card supports UHS-II Interface
    pub fn uhs_ii(&self) -> bool {
        self.0 & 0x2000_0000 != 0
    }
    /// Card Capacity Status (CCS). True for SDHC/SDXC/SDUC
    pub fn ccs(&self) -> bool {
        self.0 & 0x4000_0000 != 0
    }
    /// Card power up status bit (busy)
    pub fn is_busy(&self) -> bool {
        self.0 & 0x8000_0000 == 0 // Set active LOW
    }
}
impl fmt::Debug for OCR {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("OCR: Operation Conditions Register")
            .field(
                "Voltage Window (mV)",
                &self.voltage_window_mv().unwrap_or((0, 0)),
            )
            .field("S18A (UHS-I only)", &self.s18a())
            .field("UHS-II Card", &self.uhs_ii())
            .field("CSS", &if self.ccs() { "SDHC/SDXC/SDUC" } else { "SDSC" })
            .field("Busy", &self.is_busy())
            .finish()
    }
}
/// Card Identification Register (CID)
#[derive(Clone, Copy, Default)]
pub struct CID {
    inner: u128,
    bytes: [u8; 16],
}
impl CID {
    /// A new CID from 128 bits
    pub fn new(inner: u128) -> Self {
        Self {
            inner,
            bytes: inner.to_be_bytes(),
        }
    }
    /// Manufacturer ID
    pub fn manufacturer_id(&self) -> u8 {
        self.bytes[0]
    }
    /// OEM/Application ID
    pub fn oem_id(&self) -> &str {
        str::from_utf8(&self.bytes[1..3]).unwrap_or(&"<ERR>")
    }
    /// Product name
    pub fn product_name(&self) -> &str {
        str::from_utf8(&self.bytes[3..8]).unwrap_or(&"<ERR>")
    }
    /// Product revision
    pub fn product_revision(&self) -> u8 {
        self.bytes[8]
    }
    /// Product serial number
    pub fn product_serial_number(&self) -> u32 {
        (self.inner >> 24) as u32
    }
    /// Manufacturing date
    pub fn manufacturing_date(&self) -> (u8, u16) {
        (
            (self.inner >> 8) as u8 & 0xF,             // Month
            ((self.inner >> 12) as u16 & 0xFF) + 2000, // Year
        )
    }
}
impl fmt::Debug for CID {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("CID: Card Identification")
            .field("Manufacturer ID", &self.manufacturer_id())
            .field("OEM ID", &self.oem_id())
            .field("Product Name", &self.product_name())
            .field("Product Revision", &self.product_revision())
            .field("Product Serial Number", &self.product_serial_number())
            .field("Manufacturing Date", &self.manufacturing_date())
            .finish()
    }
}
/// Card Specific Data (CSD)
#[derive(Clone, Copy, Default)]
pub struct CSD(pub u128);
impl CSD {
    /// CSD structure version
    fn csd_version(&self) -> u8 {
        (self.0 >> 126) as u8 & 3
    }
    /// Maximum data transfer rate per one data line
    pub fn tranfer_rate(&self) -> u8 {
        (self.0 >> 96) as u8
    }
    /// Maximum block length. In an SD Memory Card the WRITE_BL_LEN is
    /// always equal to READ_BL_LEN
    pub fn block_length(&self) -> Option<BlockSize> {
        // Read block length
        match self.0 >> 80 {
            9 => Some(BlockSize::B512),
            10 => Some(BlockSize::B1024),
            11 => Some(BlockSize::B2048),
            _ => None,
        }
    }
    /// Number of blocks in the card
    pub fn block_count(&self) -> u32 {
        match self.csd_version() {
            0 => {
                // SDSC
                let c_size: u16 = ((self.0 >> 62) as u16) & 0xFFF;
                let c_size_mult: u8 = ((self.0 >> 47) as u8) & 7;

                ((c_size + 1) as u32) * ((1 << (c_size_mult + 2)) as u32)
            }
            1 => {
                // SDHC / SDXC
                ((self.0 >> 48) as u32 & 0x3F_FFFF) + 1
            }
            2 => {
                // SDUC
                ((self.0 >> 48) as u32 & 0xFFF_FFFF) + 1
            }
            _ => 0,
        }
    }
    /// Maximum read current at the minimum VDD
    pub fn read_current_minimum_vdd(&self) -> CurrentConsumption {
        CurrentConsumption::from_minimum_reg(self.0 >> 59)
    }
    /// Maximum write current at the minimum VDD
    pub fn write_current_minimum_vdd(&self) -> CurrentConsumption {
        CurrentConsumption::from_minimum_reg(self.0 >> 56)
    }
    /// Maximum read current at the maximum VDD
    pub fn read_current_maximum_vdd(&self) -> CurrentConsumption {
        CurrentConsumption::from_maximum_reg(self.0 >> 53)
    }
    /// Maximum write current at the maximum VDD
    pub fn write_current_maximum_vdd(&self) -> CurrentConsumption {
        CurrentConsumption::from_maximum_reg(self.0 >> 50)
    }
    /// Erase size (in blocks)
    pub fn erase_size_blocks(&self) -> u32 {
        if (self.0 >> 46) & 1 == 1 {
            // ERASE_BLK_EN
            1
        } else {
            let sector_size_tens = (self.0 >> 43) & 0x7;
            let sector_size_units = (self.0 >> 39) & 0xF;

            (sector_size_tens as u32 * 10) + (sector_size_units as u32)
        }
    }
}
impl fmt::Debug for CSD {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("CSD: Card Specific Data")
            .field("Tranfer Rate", &self.tranfer_rate())
            .field("Block Count", &self.block_count())
            .field("Read I (@min VDD)", &self.read_current_minimum_vdd())
            .field("Write I (@min VDD)", &self.write_current_minimum_vdd())
            .field("Read I (@max VDD)", &self.read_current_maximum_vdd())
            .field("Write I (@max VDD)", &self.write_current_maximum_vdd())
            .field("Erase Size (Blocks)", &self.erase_size_blocks())
            .finish()
    }
}
/// SD CARD Configuration Register (SCR)
#[derive(Clone, Copy, Default)]
pub struct SCR(pub u64);
impl SCR {
    /// Physical Layer Specification Version Number
    pub fn version(&self) -> CardVersion {
        let spec = (self.0 >> 56) & 0xF;
        let spec3 = (self.0 >> 47) & 1;
        let spec4 = (self.0 >> 42) & 1;
        let specx = (self.0 >> 38) & 0xF;

        // Ref PLSS_v7_10 Table 5-17
        match (spec, spec3, spec4, specx) {
            (0, 0, 0, 0) => CardVersion::V1_0,
            (1, 0, 0, 0) => CardVersion::V1_1,
            (2, 0, 0, 0) => CardVersion::V2,
            (2, 1, 0, 0) => CardVersion::V3,
            (2, 1, 1, 0) => CardVersion::V4,
            (2, 1, _, 1) => CardVersion::V5,
            (2, 1, _, 2) => CardVersion::V6,
            (2, 1, _, 3) => CardVersion::V7,
            _ => CardVersion::Unknown,
        }
    }
    /// Supports 1-bit bus width
    pub fn bus_width_one(&self) -> bool {
        (self.0 >> 48) & 1 != 0
    }
    /// Supports 4-bit bus width
    pub fn bus_width_four(&self) -> bool {
        (self.0 >> 50) & 1 != 0
    }
}
impl fmt::Debug for SCR {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SCR: SD CARD Configuration Register")
            .field("Version", &self.version())
            .field("1-bit width", &self.bus_width_one())
            .field("4-bit width", &self.bus_width_four())
            .finish()
    }
}
/// SD Status
#[derive(Clone, Copy, Default)]
pub struct SDStatus {
    inner: [u32; 16],
}
impl SDStatus {
    /// A new SD Status from a slice (512 bits)
    pub fn new(inner: [u32; 16]) -> Self {
        SDStatus { inner }
    }
    /// SDHC / SDXC: Capacity of Protected Area in bytes
    pub fn size_of_protected_area(&self) -> u32 {
        u32::from_be(self.inner[1])
    }
    /// Speed Class
    pub fn speed_class(&self) -> u8 {
        self.inner[2] as u8
    }
    /// "Performance Move" indicator in 1 MB/s units
    pub fn move_performance(&self) -> u8 {
        (self.inner[2] >> 8) as u8
    }
    /// Allocation Unit (AU) size. Lookup in PLSS v7_10 Table 4-47
    pub fn allocation_unit_size(&self) -> u8 {
        (self.inner[3] >> 20) as u8 & 0xF
    }
    /// Indicates N_Erase, in units of AU
    pub fn erase_size(&self) -> u16 {
        ((self.inner[2] & 0xFF00_0000) >> 16) as u16
            | (self.inner[3] & 0xFF) as u16
    }
    /// Indicates T_Erase
    pub fn erase_timeout(&self) -> u8 {
        (self.inner[3] >> 10) as u8 & 0x3F
    }
}
impl fmt::Debug for SDStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SD Status")
            .field("Protected Area Size (B)", &self.size_of_protected_area())
            .field("Speed Class", &self.speed_class())
            .field("Move Performance (MB/s)", &self.move_performance())
            .field("AU Size", &self.allocation_unit_size())
            .field("Erase Size (AU)", &self.erase_size())
            .field("Erase Timeout (s)", &self.erase_timeout())
            .finish()
    }
}