use crate::error::{CardError, Error, ErrorContext, Phase};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResponseType {
None,
R1,
R1b,
R2,
R3,
R4,
R5,
R6,
R7,
}
#[derive(Debug, Clone, Copy)]
pub enum Response {
None,
R1(R1Response),
R1b(R1Response),
R2([u8; 16]),
R3(OcrResponse),
R4(SdioOcrResponse),
R5(SdioRwResponse),
R6(RcaResponse),
R7(IfCondResponse),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct R1Response {
pub raw: u32,
}
impl R1Response {
pub fn from_native_raw(raw: u32) -> Result<Self, Error> {
let err_bits = ((raw >> 19) & 0x3F) as u8;
if err_bits != 0 {
return Err(Error::CardError(decode_native_card_error(err_bits)));
}
Ok(Self { raw })
}
pub fn from_spi_byte(byte: u8) -> Result<Self, Error> {
if byte & 0x80 != 0 {
return Err(Error::BadResponse(ErrorContext::new(Phase::ResponseWait)));
}
Ok(Self { raw: byte as u32 })
}
pub fn spi_card_error(&self) -> Option<CardError> {
let bits = (self.raw as u8) & 0b0111_1110;
if bits == 0 {
None
} else {
Some(decode_spi_card_error(bits))
}
}
#[deprecated(note = "use from_native_raw or from_spi_byte instead")]
pub fn from_raw(raw: u32) -> Result<Self, Error> {
if raw > 0xFF {
Self::from_native_raw(raw)
} else {
Self::from_spi_byte(raw as u8)
}
}
pub fn idle(&self) -> bool {
self.raw & (1 << 0) != 0
}
pub fn erase_reset(&self) -> bool {
self.raw & (1 << 1) != 0
}
pub fn illegal_command(&self) -> bool {
self.raw & (1 << 2) != 0
}
pub fn command_crc_failed(&self) -> bool {
self.raw & (1 << 3) != 0
}
pub fn current_state(&self) -> CardState {
match ((self.raw >> 9) & 0xF) as u8 {
0 => CardState::Idle,
1 => CardState::Ready,
2 => CardState::Identification,
3 => CardState::Standby,
4 => CardState::Transfer,
5 => CardState::SendingData,
6 => CardState::ReceiveData,
7 => CardState::Programming,
8 => CardState::Disconnect,
other => CardState::Reserved(other),
}
}
pub fn card_is_locked(&self) -> bool {
self.raw & (1 << 19) != 0
}
pub fn ready_for_data(&self) -> bool {
self.raw & (1 << 8) != 0
}
pub fn switch_error(&self) -> bool {
self.raw & (1 << 7) != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CardState {
Idle,
Ready,
Identification,
Standby,
Transfer,
SendingData,
ReceiveData,
Programming,
Disconnect,
Reserved(u8),
}
#[derive(Debug, Clone, Copy)]
pub struct OcrResponse {
pub raw: u32,
}
impl OcrResponse {
pub fn from_raw(raw: u32) -> Self {
Self { raw }
}
pub fn card_powered_up(&self) -> bool {
self.raw & (1 << 31) != 0
}
pub fn ccs(&self) -> bool {
self.raw & (1 << 30) != 0
}
pub fn voltage_window(&self) -> u32 {
self.raw & 0x00FF_FF00
}
pub fn vdd_35_36(&self) -> bool {
self.raw & (1 << 23) != 0
}
pub fn vdd_34_35(&self) -> bool {
self.raw & (1 << 22) != 0
}
pub fn vdd_33_34(&self) -> bool {
self.raw & (1 << 21) != 0
}
pub fn vdd_32_33(&self) -> bool {
self.raw & (1 << 20) != 0
}
pub fn supports_2v7_to_3v6(&self) -> bool {
self.raw & 0x00FF_8000 != 0
}
pub fn uhs2(&self) -> bool {
self.raw & (1 << 29) != 0
}
pub fn s18a(&self) -> bool {
self.raw & (1 << 24) != 0
}
}
#[derive(Debug, Clone, Copy)]
pub struct RcaResponse {
pub raw: u32,
}
impl RcaResponse {
pub fn from_raw(raw: u32) -> Self {
Self { raw }
}
pub fn rca(&self) -> u16 {
((self.raw >> 16) & 0xFFFF) as u16
}
pub fn status(&self) -> u16 {
(self.raw & 0xFFFF) as u16
}
}
#[derive(Debug, Clone, Copy)]
pub struct IfCondResponse {
pub raw: u32,
}
impl IfCondResponse {
pub fn from_raw(raw: u32) -> Self {
Self { raw }
}
pub fn voltage(&self) -> u8 {
((self.raw >> 8) & 0xF) as u8
}
pub fn check_pattern(&self) -> u8 {
(self.raw & 0xFF) as u8
}
pub fn verify(&self, voltage: u8, pattern: u8) -> bool {
self.voltage() == voltage && self.check_pattern() == pattern
}
}
#[derive(Debug, Clone, Copy)]
pub struct CsdResponse {
pub raw: [u8; 16],
}
impl CsdResponse {
pub fn from_raw(raw: [u8; 16]) -> Self {
Self { raw }
}
pub fn version(&self) -> u8 {
(self.raw[0] >> 6) & 0x03
}
pub fn capacity_blocks(&self) -> Option<u64> {
match self.version() {
0 => Some(self.csd_v1_capacity_blocks()),
1 => Some(self.csd_v2_capacity_blocks()),
_ => None,
}
}
fn csd_v1_capacity_blocks(&self) -> u64 {
let read_bl_len = (self.raw[5] & 0x0F) as u32;
let c_size = (((self.raw[6] & 0x03) as u32) << 10)
| ((self.raw[7] as u32) << 2)
| ((self.raw[8] as u32) >> 6);
let c_size_mult = (((self.raw[9] & 0x03) as u32) << 1) | ((self.raw[10] as u32) >> 7);
let mult = 1u64 << (c_size_mult + 2);
let block_len = 1u64 << read_bl_len;
let bytes = (c_size as u64 + 1) * mult * block_len;
bytes / 512
}
fn csd_v2_capacity_blocks(&self) -> u64 {
let c_size = (((self.raw[7] & 0x3F) as u32) << 16)
| ((self.raw[8] as u32) << 8)
| (self.raw[9] as u32);
(c_size as u64 + 1) * 1024
}
}
#[derive(Debug, Clone, Copy)]
pub struct CidResponse {
pub raw: [u8; 16],
}
impl CidResponse {
pub fn from_raw(raw: [u8; 16]) -> Self {
Self { raw }
}
pub fn manufacturer_id(&self) -> u8 {
self.raw[0]
}
pub fn oem_id(&self) -> [u8; 2] {
[self.raw[1], self.raw[2]]
}
pub fn product_name(&self) -> [u8; 5] {
[
self.raw[3],
self.raw[4],
self.raw[5],
self.raw[6],
self.raw[7],
]
}
pub fn product_revision(&self) -> (u8, u8) {
(self.raw[8] >> 4, self.raw[8] & 0x0F)
}
pub fn serial_number(&self) -> u32 {
u32::from_be_bytes([self.raw[9], self.raw[10], self.raw[11], self.raw[12]])
}
pub fn manufacture_date(&self) -> (u16, u8) {
let year = ((self.raw[13] & 0x0F) << 4) | (self.raw[14] >> 4);
let month = self.raw[14] & 0x0F;
(2000 + year as u16, month)
}
}
#[derive(Debug, Clone, Copy)]
pub struct SwitchStatus {
pub raw: [u8; 64],
}
impl SwitchStatus {
pub fn from_raw(raw: [u8; 64]) -> Self {
Self { raw }
}
pub fn selected_function(&self, group: u8) -> u8 {
match group {
1 => self.raw[16] & 0x0F,
2 => self.raw[16] >> 4,
3 => self.raw[15] & 0x0F,
4 => self.raw[15] >> 4,
5 => self.raw[14] & 0x0F,
6 => self.raw[14] >> 4,
_ => 0xF,
}
}
pub fn high_speed_active(&self) -> bool {
self.selected_function(1) == 1
}
pub fn access_mode_supported(&self, function: u8) -> bool {
function < 8 && (self.raw[13] & (1 << function)) != 0
}
}
#[derive(Debug, Clone, Copy)]
pub struct SdioOcrResponse {
pub raw: u32,
}
impl SdioOcrResponse {
pub fn from_raw(raw: u32) -> Self {
Self { raw }
}
pub fn io_functions(&self) -> u8 {
((self.raw >> 28) & 0x7) as u8
}
pub fn memory_present(&self) -> bool {
self.raw & (1 << 27) != 0
}
pub fn io_ready(&self) -> bool {
self.raw & (1 << 31) != 0
}
}
#[derive(Debug, Clone, Copy)]
pub struct SdioRwResponse {
pub raw: u32,
}
impl SdioRwResponse {
pub fn from_raw(raw: u32) -> Self {
Self { raw }
}
pub fn data(&self) -> u8 {
(self.raw & 0xFF) as u8
}
pub fn flags(&self) -> u8 {
((self.raw >> 8) & 0xFF) as u8
}
}
fn decode_spi_card_error(bits: u8) -> CardError {
if bits & 0b0000_1000 != 0 {
CardError::CommandCrcFailed
} else if bits & 0b0000_0100 != 0 {
CardError::IllegalCommand
} else if bits & 0b0010_0000 != 0 {
CardError::AddressError
} else if bits & 0b0100_0000 != 0 {
CardError::AddressError
} else if bits & (0b0001_0000 | 0b0000_0010) != 0 {
CardError::EraseSequence
} else {
CardError::Unknown(bits)
}
}
fn decode_native_card_error(bits: u8) -> CardError {
if bits & 0b0001_0000 != 0 {
CardError::CommandCrcFailed
} else if bits & 0b0000_1000 != 0 {
CardError::IllegalCommand
} else if bits & 0b0000_0100 != 0 {
CardError::CardEccFailed
} else if bits & 0b0000_0010 != 0 {
CardError::ControllerError
} else if bits & 0b0010_0000 != 0 {
CardError::ControllerError
} else if bits & 0b0000_0001 != 0 {
CardError::EraseSequence
} else {
CardError::Unknown(bits)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn spi_r1_idle_uses_bit_zero() {
let response = R1Response::from_spi_byte(0x01).unwrap();
assert!(response.idle());
assert!(!response.illegal_command());
assert!(response.spi_card_error().is_none());
}
#[test]
fn spi_r1_illegal_command_sets_flag_and_card_error() {
let response = R1Response::from_spi_byte(0x04).unwrap();
assert!(response.illegal_command());
assert_eq!(response.spi_card_error(), Some(CardError::IllegalCommand));
}
#[test]
fn spi_r1_idle_plus_illegal_command_preserves_both() {
let response = R1Response::from_spi_byte(0x05).unwrap();
assert!(response.idle());
assert!(response.illegal_command());
assert_eq!(response.spi_card_error(), Some(CardError::IllegalCommand));
}
#[test]
fn spi_r1_high_bit_is_bus_error() {
assert!(matches!(
R1Response::from_spi_byte(0x80),
Err(Error::BadResponse(_))
));
assert!(matches!(
R1Response::from_spi_byte(0xFF),
Err(Error::BadResponse(_))
));
}
#[test]
fn native_r1_status_bits_decoded() {
let r1 = R1Response::from_native_raw(4 << 9).unwrap();
assert_eq!(r1.current_state(), CardState::Transfer);
}
#[test]
fn native_r1_with_illegal_command_returns_error() {
let err = R1Response::from_native_raw(1 << 22).unwrap_err();
assert_eq!(err, Error::CardError(CardError::IllegalCommand));
}
#[test]
fn decode_spi_card_error_priority_handles_multiple_bits() {
assert_eq!(
decode_spi_card_error(0b0000_1100),
CardError::CommandCrcFailed
);
}
#[test]
fn decode_spi_card_error_unknown_for_unrecognized_bits() {
assert_eq!(decode_spi_card_error(0b0000_0000), CardError::Unknown(0));
}
#[test]
fn csd_v2_decodes_2gib_capacity() {
let mut raw = [0u8; 16];
raw[0] = 0x40;
raw[7] = 0x00;
raw[8] = 0x0F;
raw[9] = 0x0F;
let csd = CsdResponse::from_raw(raw);
assert_eq!(csd.version(), 1);
assert_eq!(csd.capacity_blocks(), Some((0x0F0F + 1) * 1024));
}
#[test]
fn csd_v1_decodes_known_capacity() {
let mut raw = [0u8; 16];
raw[0] = 0x00; raw[5] = 0x09; raw[6] = 0b0000_0011; raw[6] = 0b0000_0011;
raw[7] = 0xBF;
raw[8] = 0b1100_0000;
raw[9] = 0b0000_0011;
raw[10] = 0b1000_0000;
let csd = CsdResponse::from_raw(raw);
assert_eq!(csd.version(), 0);
let expected = (0x0EFFu64 + 1) * (1 << (7 + 2)) * (1 << 9) / 512;
assert_eq!(csd.capacity_blocks(), Some(expected));
}
#[test]
fn csd_unknown_version_returns_none() {
let mut raw = [0u8; 16];
raw[0] = 0x80; let csd = CsdResponse::from_raw(raw);
assert_eq!(csd.version(), 2);
assert_eq!(csd.capacity_blocks(), None);
}
#[test]
fn cid_decodes_manufacturer_oem_product_serial_and_date() {
let mut raw = [0u8; 16];
raw[0] = 0x03;
raw[1] = b'S';
raw[2] = b'D';
raw[3] = b'A';
raw[4] = b'B';
raw[5] = b'C';
raw[6] = b'1';
raw[7] = b'2';
raw[8] = (2 << 4) | 7;
raw[9] = 0xDE;
raw[10] = 0xAD;
raw[11] = 0xBE;
raw[12] = 0xEF;
raw[13] = 0x01; raw[14] = 0xA5;
let cid = CidResponse::from_raw(raw);
assert_eq!(cid.manufacturer_id(), 0x03);
assert_eq!(&cid.oem_id(), b"SD");
assert_eq!(&cid.product_name(), b"ABC12");
assert_eq!(cid.product_revision(), (2, 7));
assert_eq!(cid.serial_number(), 0xDEAD_BEEF);
assert_eq!(cid.manufacture_date(), (2026, 5));
}
#[test]
fn switch_status_reports_high_speed_when_group_one_function_one() {
let mut raw = [0u8; 64];
raw[16] = 0x01; let status = SwitchStatus::from_raw(raw);
assert_eq!(status.selected_function(1), 1);
assert!(status.high_speed_active());
}
#[test]
fn switch_status_reports_access_mode_support_bits() {
let mut raw = [0u8; 64];
raw[13] = (1 << 1) | (1 << 3);
let status = SwitchStatus::from_raw(raw);
assert!(status.access_mode_supported(1));
assert!(status.access_mode_supported(3));
assert!(!status.access_mode_supported(2));
assert!(!status.access_mode_supported(8));
}
#[test]
fn switch_status_reports_default_when_group_one_function_zero() {
let raw = [0u8; 64];
let status = SwitchStatus::from_raw(raw);
assert_eq!(status.selected_function(1), 0);
assert!(!status.high_speed_active());
}
#[test]
fn switch_status_unsupported_group_returns_0xf() {
let mut raw = [0u8; 64];
raw[16] = 0xF0; let status = SwitchStatus::from_raw(raw);
assert_eq!(status.selected_function(2), 0xF);
assert_eq!(status.selected_function(7), 0xF); }
}