pub const ERROR_OK: u8 = 0x00;
pub const CMD0: u8 = 0x00;
pub const CMD8: u8 = 0x08;
pub const CMD9: u8 = 0x09;
pub const CMD12: u8 = 0x0C;
pub const CMD13: u8 = 0x0D;
pub const CMD17: u8 = 0x11;
pub const CMD18: u8 = 0x12;
pub const CMD24: u8 = 0x18;
pub const CMD25: u8 = 0x19;
pub const CMD55: u8 = 0x37;
pub const CMD58: u8 = 0x3A;
pub const CMD59: u8 = 0x3B;
pub const ACMD23: u8 = 0x17;
pub const ACMD41: u8 = 0x29;
pub const R1_READY_STATE: u8 = 0x00;
pub const R1_IDLE_STATE: u8 = 0x01;
pub const R1_ILLEGAL_COMMAND: u8 = 0x04;
pub const DATA_START_BLOCK: u8 = 0xFE;
pub const STOP_TRAN_TOKEN: u8 = 0xFD;
pub const WRITE_MULTIPLE_TOKEN: u8 = 0xFC;
pub const DATA_RES_MASK: u8 = 0x1F;
pub const DATA_RES_ACCEPTED: u8 = 0x05;
#[derive(Default, Debug)]
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
pub struct CsdV1 {
pub data: [u8; 16],
}
#[derive(Default, Debug)]
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
pub struct CsdV2 {
pub data: [u8; 16],
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
pub enum Csd {
V1(CsdV1),
V2(CsdV2),
}
impl CsdV1 {
pub fn new() -> CsdV1 {
CsdV1::default()
}
define_field!(csd_ver, u8, 0, 6, 2);
define_field!(data_read_access_time1, u8, 1, 0, 8);
define_field!(data_read_access_time2, u8, 2, 0, 8);
define_field!(max_data_transfer_rate, u8, 3, 0, 8);
define_field!(card_command_classes, u16, [(4, 0, 8), (5, 4, 4)]);
define_field!(read_block_length, u8, 5, 0, 4);
define_field!(read_partial_blocks, bool, 6, 7);
define_field!(write_block_misalignment, bool, 6, 6);
define_field!(read_block_misalignment, bool, 6, 5);
define_field!(dsr_implemented, bool, 6, 4);
define_field!(device_size, u32, [(6, 0, 2), (7, 0, 8), (8, 6, 2)]);
define_field!(max_read_current_vdd_max, u8, 8, 0, 3);
define_field!(max_read_current_vdd_min, u8, 8, 3, 3);
define_field!(max_write_current_vdd_max, u8, 9, 2, 3);
define_field!(max_write_current_vdd_min, u8, 9, 5, 3);
define_field!(device_size_multiplier, u8, [(9, 0, 2), (10, 7, 1)]);
define_field!(erase_single_block_enabled, bool, 10, 6);
define_field!(erase_sector_size, u8, [(10, 0, 6), (11, 7, 1)]);
define_field!(write_protect_group_size, u8, 11, 0, 7);
define_field!(write_protect_group_enable, bool, 12, 7);
define_field!(write_speed_factor, u8, 12, 2, 3);
define_field!(max_write_data_length, u8, [(12, 0, 2), (13, 6, 2)]);
define_field!(write_partial_blocks, bool, 13, 5);
define_field!(file_format, u8, 14, 2, 2);
define_field!(temporary_write_protection, bool, 14, 4);
define_field!(permanent_write_protection, bool, 14, 5);
define_field!(copy_flag_set, bool, 14, 6);
define_field!(file_format_group_set, bool, 14, 7);
define_field!(crc, u8, 15, 0, 8);
pub fn card_capacity_bytes(&self) -> u64 {
let multiplier = self.device_size_multiplier() + self.read_block_length() + 2;
(u64::from(self.device_size()) + 1) << multiplier
}
pub fn card_capacity_blocks(&self) -> u32 {
let multiplier = self.device_size_multiplier() + self.read_block_length() - 7;
(self.device_size() + 1) << multiplier
}
}
impl CsdV2 {
pub fn new() -> CsdV2 {
CsdV2::default()
}
define_field!(csd_ver, u8, 0, 6, 2);
define_field!(data_read_access_time1, u8, 1, 0, 8);
define_field!(data_read_access_time2, u8, 2, 0, 8);
define_field!(max_data_transfer_rate, u8, 3, 0, 8);
define_field!(card_command_classes, u16, [(4, 0, 8), (5, 4, 4)]);
define_field!(read_block_length, u8, 5, 0, 4);
define_field!(read_partial_blocks, bool, 6, 7);
define_field!(write_block_misalignment, bool, 6, 6);
define_field!(read_block_misalignment, bool, 6, 5);
define_field!(dsr_implemented, bool, 6, 4);
define_field!(device_size, u32, [(7, 0, 6), (8, 0, 8), (9, 0, 8)]);
define_field!(erase_single_block_enabled, bool, 10, 6);
define_field!(erase_sector_size, u8, [(10, 0, 6), (11, 7, 1)]);
define_field!(write_protect_group_size, u8, 11, 0, 7);
define_field!(write_protect_group_enable, bool, 12, 7);
define_field!(write_speed_factor, u8, 12, 2, 3);
define_field!(max_write_data_length, u8, [(12, 0, 2), (13, 6, 2)]);
define_field!(write_partial_blocks, bool, 13, 5);
define_field!(file_format, u8, 14, 2, 2);
define_field!(temporary_write_protection, bool, 14, 4);
define_field!(permanent_write_protection, bool, 14, 5);
define_field!(copy_flag_set, bool, 14, 6);
define_field!(file_format_group_set, bool, 14, 7);
define_field!(crc, u8, 15, 0, 8);
pub fn card_capacity_bytes(&self) -> u64 {
(u64::from(self.device_size()) + 1) * 512 * 1024
}
pub fn card_capacity_blocks(&self) -> u32 {
(self.device_size() + 1) * 1024
}
}
pub fn crc7(data: &[u8]) -> u8 {
let mut crc = 0u8;
for mut d in data.iter().cloned() {
for _bit in 0..8 {
crc <<= 1;
if ((d & 0x80) ^ (crc & 0x80)) != 0 {
crc ^= 0x09;
}
d <<= 1;
}
}
(crc << 1) | 1
}
pub fn crc16(data: &[u8]) -> u16 {
let mut crc = 0u16;
for &byte in data {
crc = ((crc >> 8) & 0xFF) | (crc << 8);
crc ^= u16::from(byte);
crc ^= (crc & 0xFF) >> 4;
crc ^= crc << 12;
crc ^= (crc & 0xFF) << 5;
}
crc
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_crc7() {
const DATA: [u8; 15] = hex!("00 26 00 32 5F 59 83 C8 AD DB CF FF D2 40 40");
assert_eq!(crc7(&DATA), 0xA5);
}
#[test]
fn test_crc16() {
const DATA: [u8; 16] = hex!("00 26 00 32 5F 5A 83 AE FE FB CF FF 92 80 40 DF");
assert_eq!(crc16(&DATA), 0x9fc5);
}
#[test]
fn test_csdv1b() {
const EXAMPLE: CsdV1 = CsdV1 {
data: hex!("00 26 00 32 5F 59 83 C8 AD DB CF FF D2 40 40 A5"),
};
assert_eq!(EXAMPLE.csd_ver(), 0x00);
assert_eq!(EXAMPLE.data_read_access_time1(), 0x26);
assert_eq!(EXAMPLE.data_read_access_time2(), 0x00);
assert_eq!(EXAMPLE.max_data_transfer_rate(), 0x32);
assert_eq!(EXAMPLE.card_command_classes(), 0x5f5);
assert_eq!(EXAMPLE.read_block_length(), 0x09);
assert!(EXAMPLE.read_partial_blocks());
assert!(!EXAMPLE.write_block_misalignment());
assert!(!EXAMPLE.read_block_misalignment());
assert!(!EXAMPLE.dsr_implemented());
assert_eq!(EXAMPLE.device_size(), 3874);
assert_eq!(EXAMPLE.max_read_current_vdd_min(), 5);
assert_eq!(EXAMPLE.max_read_current_vdd_max(), 5);
assert_eq!(EXAMPLE.max_write_current_vdd_min(), 6);
assert_eq!(EXAMPLE.max_write_current_vdd_max(), 6);
assert_eq!(EXAMPLE.device_size_multiplier(), 7);
assert!(EXAMPLE.erase_single_block_enabled());
assert_eq!(EXAMPLE.erase_sector_size(), 0x1F);
assert_eq!(EXAMPLE.write_protect_group_size(), 0x7f);
assert!(EXAMPLE.write_protect_group_enable());
assert_eq!(EXAMPLE.write_speed_factor(), 0x4);
assert_eq!(EXAMPLE.max_write_data_length(), 0x9);
assert!(!EXAMPLE.write_partial_blocks());
assert!(!EXAMPLE.file_format_group_set());
assert!(EXAMPLE.copy_flag_set());
assert!(!EXAMPLE.permanent_write_protection());
assert!(!EXAMPLE.temporary_write_protection());
assert_eq!(EXAMPLE.file_format(), 0x00);
assert_eq!(EXAMPLE.crc(), 0xa5);
assert_eq!(EXAMPLE.card_capacity_bytes(), 1_015_808_000);
assert_eq!(EXAMPLE.card_capacity_blocks(), 1_984_000);
}
#[test]
fn test_csdv1() {
const EXAMPLE: CsdV1 = CsdV1 {
data: hex!("00 7F 00 32 5B 5A 83 AF 7F FF CF 80 16 80 00 6F"),
};
assert_eq!(EXAMPLE.csd_ver(), 0x00);
assert_eq!(EXAMPLE.data_read_access_time1(), 0x7F);
assert_eq!(EXAMPLE.data_read_access_time2(), 0x00);
assert_eq!(EXAMPLE.max_data_transfer_rate(), 0x32);
assert_eq!(EXAMPLE.card_command_classes(), 0x5b5);
assert_eq!(EXAMPLE.read_block_length(), 0x0a);
assert!(EXAMPLE.read_partial_blocks());
assert!(!EXAMPLE.write_block_misalignment());
assert!(!EXAMPLE.read_block_misalignment());
assert!(!EXAMPLE.dsr_implemented());
assert_eq!(EXAMPLE.device_size(), 3773);
assert_eq!(EXAMPLE.max_read_current_vdd_min(), 7);
assert_eq!(EXAMPLE.max_read_current_vdd_max(), 7);
assert_eq!(EXAMPLE.max_write_current_vdd_min(), 7);
assert_eq!(EXAMPLE.max_write_current_vdd_max(), 7);
assert_eq!(EXAMPLE.device_size_multiplier(), 7);
assert!(EXAMPLE.erase_single_block_enabled());
assert_eq!(EXAMPLE.erase_sector_size(), 0x1F);
assert_eq!(EXAMPLE.write_protect_group_size(), 0x00);
assert!(!EXAMPLE.write_protect_group_enable());
assert_eq!(EXAMPLE.write_speed_factor(), 0x5);
assert_eq!(EXAMPLE.max_write_data_length(), 0xa);
assert!(!EXAMPLE.write_partial_blocks());
assert!(!EXAMPLE.file_format_group_set());
assert!(!EXAMPLE.copy_flag_set());
assert!(!EXAMPLE.permanent_write_protection());
assert!(!EXAMPLE.temporary_write_protection());
assert_eq!(EXAMPLE.file_format(), 0x00);
assert_eq!(EXAMPLE.crc(), 0x6F);
assert_eq!(EXAMPLE.card_capacity_bytes(), 1_978_662_912);
assert_eq!(EXAMPLE.card_capacity_blocks(), 3_864_576);
}
#[test]
fn test_csdv2() {
const EXAMPLE: CsdV2 = CsdV2 {
data: hex!("40 0E 00 32 5B 59 00 00 1D 69 7F 80 0A 40 00 8B"),
};
assert_eq!(EXAMPLE.csd_ver(), 0x01);
assert_eq!(EXAMPLE.data_read_access_time1(), 0x0E);
assert_eq!(EXAMPLE.data_read_access_time2(), 0x00);
assert_eq!(EXAMPLE.max_data_transfer_rate(), 0x32);
assert_eq!(EXAMPLE.card_command_classes(), 0x5b5);
assert_eq!(EXAMPLE.read_block_length(), 0x09);
assert!(!EXAMPLE.read_partial_blocks());
assert!(!EXAMPLE.write_block_misalignment());
assert!(!EXAMPLE.read_block_misalignment());
assert!(!EXAMPLE.dsr_implemented());
assert_eq!(EXAMPLE.device_size(), 7529);
assert!(EXAMPLE.erase_single_block_enabled());
assert_eq!(EXAMPLE.erase_sector_size(), 0x7F);
assert_eq!(EXAMPLE.write_protect_group_size(), 0x00);
assert!(!EXAMPLE.write_protect_group_enable());
assert_eq!(EXAMPLE.write_speed_factor(), 0x2);
assert_eq!(EXAMPLE.max_write_data_length(), 0x9);
assert!(!EXAMPLE.write_partial_blocks());
assert!(!EXAMPLE.file_format_group_set());
assert!(!EXAMPLE.copy_flag_set());
assert!(!EXAMPLE.permanent_write_protection());
assert!(!EXAMPLE.temporary_write_protection());
assert_eq!(EXAMPLE.file_format(), 0x00);
assert_eq!(EXAMPLE.crc(), 0x8b);
assert_eq!(EXAMPLE.card_capacity_bytes(), 3_947_888_640);
assert_eq!(EXAMPLE.card_capacity_blocks(), 7_710_720);
}
#[test]
fn test_csdv2b() {
const EXAMPLE: CsdV2 = CsdV2 {
data: hex!("40 0E 00 32 5B 59 00 00 3A 91 7F 80 0A 40 00 05"),
};
assert_eq!(EXAMPLE.csd_ver(), 0x01);
assert_eq!(EXAMPLE.data_read_access_time1(), 0x0E);
assert_eq!(EXAMPLE.data_read_access_time2(), 0x00);
assert_eq!(EXAMPLE.max_data_transfer_rate(), 0x32);
assert_eq!(EXAMPLE.card_command_classes(), 0x5b5);
assert_eq!(EXAMPLE.read_block_length(), 0x09);
assert!(!EXAMPLE.read_partial_blocks());
assert!(!EXAMPLE.write_block_misalignment());
assert!(!EXAMPLE.read_block_misalignment());
assert!(!EXAMPLE.dsr_implemented());
assert_eq!(EXAMPLE.device_size(), 14993);
assert!(EXAMPLE.erase_single_block_enabled());
assert_eq!(EXAMPLE.erase_sector_size(), 0x7F);
assert_eq!(EXAMPLE.write_protect_group_size(), 0x00);
assert!(!EXAMPLE.write_protect_group_enable());
assert_eq!(EXAMPLE.write_speed_factor(), 0x2);
assert_eq!(EXAMPLE.max_write_data_length(), 0x9);
assert!(!EXAMPLE.write_partial_blocks());
assert!(!EXAMPLE.file_format_group_set());
assert!(!EXAMPLE.copy_flag_set());
assert!(!EXAMPLE.permanent_write_protection());
assert!(!EXAMPLE.temporary_write_protection());
assert_eq!(EXAMPLE.file_format(), 0x00);
assert_eq!(EXAMPLE.crc(), 0x05);
assert_eq!(EXAMPLE.card_capacity_bytes(), 7_861_174_272);
assert_eq!(EXAMPLE.card_capacity_blocks(), 15_353_856);
}
}