pub const ERROR_OK: u8 = 0x00;
pub const ERROR_CMD0: u8 = 0x20;
pub const ERROR_CMD2: u8 = 0x21;
pub const ERROR_CMD3: u8 = 0x22;
pub const ERROR_CMD6: u8 = 0x23;
pub const ERROR_CMD7: u8 = 0x24;
pub const ERROR_CMD8: u8 = 0x25;
pub const ERROR_CMD9: u8 = 0x26;
pub const ERROR_CMD10: u8 = 0x27;
pub const ERROR_CMD12: u8 = 0x28;
pub const ERROR_CMD13: u8 = 0x29;
pub const ERROR_CMD17: u8 = 0x30;
pub const ERROR_CMD18: u8 = 0x31;
pub const ERROR_CMD24: u8 = 0x32;
pub const ERROR_CMD25: u8 = 0x33;
pub const ERROR_CMD32: u8 = 0x34;
pub const ERROR_CMD33: u8 = 0x35;
pub const ERROR_CMD38: u8 = 0x36;
pub const ERROR_CMD58: u8 = 0x37;
pub const ERROR_CMD59: u8 = 0x38;
pub const ERROR_ACMD6: u8 = 0x40;
pub const ERROR_ACMD13: u8 = 0x41;
pub const ERROR_ACMD23: u8 = 0x42;
pub const ERROR_ACMD41: u8 = 0x43;
pub const ERROR_READ: u8 = 0x50;
pub const ERROR_READ_CRC: u8 = 0x51;
pub const ERROR_READ_FIFO: u8 = 0x52;
pub const ERROR_READ_REG: u8 = 0x53;
pub const ERROR_READ_START: u8 = 0x54;
pub const ERROR_READ_TIMEOUT: u8 = 0x55;
pub const ERROR_STOP_TRAN: u8 = 0x56;
pub const ERROR_WRITE: u8 = 0x57;
pub const ERROR_WRITE_FIFO: u8 = 0x58;
pub const ERROR_WRITE_START: u8 = 0x59;
pub const ERROR_WRITE_TIMEOUT: u8 = 0x60;
pub const ERROR_DMA: u8 = 0x60;
pub const ERROR_ERASE: u8 = 0x61;
pub const ERROR_ERASE_SINGLE_BLOCK: u8 = 0x61;
pub const ERROR_ERASE_TIMEOUT: u8 = 0x62;
pub const ERROR_INIT_NOT_CALLED: u8 = 0x63;
pub const ERROR_FUNCTION_NOT_SUPPORTED: u8 = 0x64;
#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum CardType {
SD1 = 1,
SD2 = 2,
SDHC = 3,
}
pub const CMD0: u8 = 0x00;
pub const CMD2: u8 = 0x02;
pub const CMD3: u8 = 0x03;
pub const CMD6: u8 = 0x06;
pub const CMD7: u8 = 0x07;
pub const CMD8: u8 = 0x08;
pub const CMD9: u8 = 0x09;
pub const CMD10: u8 = 0x0A;
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 CMD32: u8 = 0x20;
pub const CMD33: u8 = 0x21;
pub const CMD38: u8 = 0x26;
pub const CMD55: u8 = 0x37;
pub const CMD58: u8 = 0x3A;
pub const CMD59: u8 = 0x3B;
pub const ACMD6: u8 = 0x06;
pub const ACMD13: u8 = 0x0D;
pub const ACMD23: u8 = 0x17;
pub const ACMD41: u8 = 0x29;
pub const CARD_STATUS_OUT_OF_RANGE: u32 = 1 << 31;
pub const CARD_STATUS_ADDRESS_ERROR: u32 = 1 << 30;
pub const CARD_STATUS_BLOCK_LEN_ERROR: u32 = 1 << 29;
pub const CARD_STATUS_ERASE_SEQ_ERROR: u32 = 1 << 28;
pub const CARD_STATUS_ERASE_PARAM: u32 = 1 << 27;
pub const CARD_STATUS_WP_VIOLATION: u32 = 1 << 26;
pub const CARD_STATUS_CARD_IS_LOCKED: u32 = 1 << 25;
pub const CARD_STATUS_LOCK_UNLOCK_FAILED: u32 = 1 << 24;
pub const CARD_STATUS_COM_CRC_ERROR: u32 = 1 << 23;
pub const CARD_STATUS_ILLEGAL_COMMAND: u32 = 1 << 22;
pub const CARD_STATUS_CARD_ECC_FAILED: u32 = 1 << 21;
pub const CARD_STATUS_CC_ERROR: u32 = 1 << 20;
pub const CARD_STATUS_ERROR: u32 = 1 << 19;
pub const CARD_STATUS_CSD_OVERWRITE: u32 = 1 << 16;
pub const CARD_STATUS_WP_ERASE_SKIP: u32 = 1 << 15;
pub const CARD_STATUS_CARD_ECC_DISABLED: u32 = 1 << 14;
pub const CARD_STATUS_ERASE_RESET: u32 = 1 << 13;
pub const CARD_STATUS_CURRENT_STATE: u32 = 0xF << 9;
pub const CARD_STATUS_CURRENT_STATE_SHIFT: u32 = 9;
pub const CARD_STATUS_READY_FOR_DATA: u32 = 1 << 8;
pub const CARD_STATUS_FX_EVENT: u32 = 1 << 6;
pub const CARD_STATUS_APP_CMD: u32 = 1 << 5;
pub const CARD_STATUS_AKE_SEQ_ERROR: u32 = 1 << 3;
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;
#[repr(packed)]
pub struct Cid {
pub mid: u8,
pub oid: [u8; 2],
pub pnm: [u8; 5],
pub prv: u8,
pub psn: u32,
pub mdt_year_high: u8,
pub mdt_year_low_month: u8,
pub crc: u8,
}
pub struct CsdV1 {
pub data: [u8; 16],
}
pub struct CsdV2 {
pub data: [u8; 16],
}
pub enum Csd {
V1(CsdV1),
V2(CsdV2),
}
impl CsdV1 {
pub fn new() -> CsdV1 {
CsdV1 { data: [0u8; 16] }
}
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 { data: [0u8; 16] }
}
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] = [
0x00, 0x26, 0x00, 0x32, 0x5f, 0x59, 0x83, 0xc8, 0xad, 0xdb, 0xcf, 0xff, 0xd2, 0x40,
0x40,
];
assert_eq!(crc7(&DATA), 0xA5);
}
#[test]
fn test_crc16() {
const DATA: [u8; 16] = [
0x00, 0x26, 0x00, 0x32, 0x5f, 0x5a, 0x83, 0xae, 0xfe, 0xfb, 0xcf, 0xff, 0x92, 0x80,
0x40, 0xdf,
];
assert_eq!(crc16(&DATA), 0x9fc5);
}
#[test]
fn test_csdv1b() {
const EXAMPLE: CsdV1 = CsdV1 {
data: [
0x00, 0x26, 0x00, 0x32, 0x5f, 0x59, 0x83, 0xc8, 0xad, 0xdb, 0xcf, 0xff, 0xd2, 0x40,
0x40, 0xa5,
],
};
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_eq!(EXAMPLE.read_partial_blocks(), true);
assert_eq!(EXAMPLE.write_block_misalignment(), false);
assert_eq!(EXAMPLE.read_block_misalignment(), false);
assert_eq!(EXAMPLE.dsr_implemented(), false);
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_eq!(EXAMPLE.erase_single_block_enabled(), true);
assert_eq!(EXAMPLE.erase_sector_size(), 0x1F);
assert_eq!(EXAMPLE.write_protect_group_size(), 0x7f);
assert_eq!(EXAMPLE.write_protect_group_enable(), true);
assert_eq!(EXAMPLE.write_speed_factor(), 0x4);
assert_eq!(EXAMPLE.max_write_data_length(), 0x9);
assert_eq!(EXAMPLE.write_partial_blocks(), false);
assert_eq!(EXAMPLE.file_format_group_set(), false);
assert_eq!(EXAMPLE.copy_flag_set(), true);
assert_eq!(EXAMPLE.permanent_write_protection(), false);
assert_eq!(EXAMPLE.temporary_write_protection(), false);
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: [
0x00, 0x7f, 0x00, 0x32, 0x5b, 0x5a, 0x83, 0xaf, 0x7f, 0xff, 0xcf, 0x80, 0x16, 0x80,
0x00, 0x6f,
],
};
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_eq!(EXAMPLE.read_partial_blocks(), true);
assert_eq!(EXAMPLE.write_block_misalignment(), false);
assert_eq!(EXAMPLE.read_block_misalignment(), false);
assert_eq!(EXAMPLE.dsr_implemented(), false);
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_eq!(EXAMPLE.erase_single_block_enabled(), true);
assert_eq!(EXAMPLE.erase_sector_size(), 0x1F);
assert_eq!(EXAMPLE.write_protect_group_size(), 0x00);
assert_eq!(EXAMPLE.write_protect_group_enable(), false);
assert_eq!(EXAMPLE.write_speed_factor(), 0x5);
assert_eq!(EXAMPLE.max_write_data_length(), 0xa);
assert_eq!(EXAMPLE.write_partial_blocks(), false);
assert_eq!(EXAMPLE.file_format_group_set(), false);
assert_eq!(EXAMPLE.copy_flag_set(), false);
assert_eq!(EXAMPLE.permanent_write_protection(), false);
assert_eq!(EXAMPLE.temporary_write_protection(), false);
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: [
0x40, 0x0e, 0x00, 0x32, 0x5b, 0x59, 0x00, 0x00, 0x1d, 0x69, 0x7f, 0x80, 0x0a, 0x40,
0x00, 0x8b,
],
};
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_eq!(EXAMPLE.read_partial_blocks(), false);
assert_eq!(EXAMPLE.write_block_misalignment(), false);
assert_eq!(EXAMPLE.read_block_misalignment(), false);
assert_eq!(EXAMPLE.dsr_implemented(), false);
assert_eq!(EXAMPLE.device_size(), 7529);
assert_eq!(EXAMPLE.erase_single_block_enabled(), true);
assert_eq!(EXAMPLE.erase_sector_size(), 0x7F);
assert_eq!(EXAMPLE.write_protect_group_size(), 0x00);
assert_eq!(EXAMPLE.write_protect_group_enable(), false);
assert_eq!(EXAMPLE.write_speed_factor(), 0x2);
assert_eq!(EXAMPLE.max_write_data_length(), 0x9);
assert_eq!(EXAMPLE.write_partial_blocks(), false);
assert_eq!(EXAMPLE.file_format_group_set(), false);
assert_eq!(EXAMPLE.copy_flag_set(), false);
assert_eq!(EXAMPLE.permanent_write_protection(), false);
assert_eq!(EXAMPLE.temporary_write_protection(), false);
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: [
0x40, 0x0e, 0x00, 0x32, 0x5b, 0x59, 0x00, 0x00, 0x3a, 0x91, 0x7f, 0x80, 0x0a, 0x40,
0x00, 0x05,
],
};
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_eq!(EXAMPLE.read_partial_blocks(), false);
assert_eq!(EXAMPLE.write_block_misalignment(), false);
assert_eq!(EXAMPLE.read_block_misalignment(), false);
assert_eq!(EXAMPLE.dsr_implemented(), false);
assert_eq!(EXAMPLE.device_size(), 14993);
assert_eq!(EXAMPLE.erase_single_block_enabled(), true);
assert_eq!(EXAMPLE.erase_sector_size(), 0x7F);
assert_eq!(EXAMPLE.write_protect_group_size(), 0x00);
assert_eq!(EXAMPLE.write_protect_group_enable(), false);
assert_eq!(EXAMPLE.write_speed_factor(), 0x2);
assert_eq!(EXAMPLE.max_write_data_length(), 0x9);
assert_eq!(EXAMPLE.write_partial_blocks(), false);
assert_eq!(EXAMPLE.file_format_group_set(), false);
assert_eq!(EXAMPLE.copy_flag_set(), false);
assert_eq!(EXAMPLE.permanent_write_protection(), false);
assert_eq!(EXAMPLE.temporary_write_protection(), false);
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);
}
}