use drone_core::bitfield::Bitfield;
use errors::{R1Error, ReadError, WriteError};
pub const BLOCK_START: u8 = 0xFE;
pub const BLOCK_START_MULTI_WRITE: u8 = 0xFC;
pub const STOP_TRAN: u8 = 0xFD;
const CSD20_C_MULT: u32 = 8;
const CSD20_READ_BL_LEN: u16 = 9;
const CSD20_WRITE_BL_LEN: u16 = 9;
const CSD20_SECTOR_SIZE: u32 = 0x7F;
#[derive(Clone, Copy, Bitfield)]
#[bitfield(
supports_2v8(rw, 15, "Supports 2.7-2.8V."),
supports_2v9(rw, 16, "Supports 2.8-2.9V."),
supports_3v0(rw, 17, "Supports 2.9-3.0V."),
supports_3v1(rw, 18, "Supports 3.0-3.1V."),
supports_3v2(rw, 19, "Supports 3.1-3.2V."),
supports_3v3(rw, 20, "Supports 3.2-3.3V."),
supports_3v4(rw, 21, "Supports 3.3-3.4V."),
supports_3v5(rw, 22, "Supports 3.4-3.5V."),
supports_3v6(rw, 23, "Supports 3.5-3.6V."),
s18a(r, 24, "Switching to 1.8V Accepted."),
card_status(r, 29, "UHS-II Card Status."),
ccs(r, 30, "Card Capacity Status."),
busy(r, 31, "Card power up status.")
)]
pub struct Ocr(u32);
#[derive(Clone, Copy, Bitfield)]
#[bitfield(
crc(
rw,
1,
7,
"The CRC field carries the check sum for the CSD contents."
),
file_format(rw, 10, 2, "Indicates the file format on the card."),
tmp_write_protect(
rw,
12,
1,
"Temporarily protects the entire card content from being overwritten or \
erased (all write and erase commands for this card are temporarily \
disabled)."
),
perm_write_protect(
rw,
13,
1,
"Permanently protects the entire card content against overwriting or \
erasing (all write and erase commands for this card are permanently \
disabled)."
),
copy(
rw,
14,
1,
"Defines whether the contents is original (`0`) or has been copied (`1`)."
),
file_format_grp(rw, 15, 1, "Indicates the selected group of file formats."),
write_bl_partial(
r,
21,
1,
"Defines whether partial block sizes can be used in block write commands."
),
write_bl_len(
r,
22,
4,
"The maximum write data block length is computed as \
2<sup>`WRITE_BL_LEN`</sup>."
),
r2w_factor(
r,
26,
3,
"Defines the typical block program time as a multiple of the read access \
time."
),
wp_grp_enable(
r,
31,
1,
"A value of 0 means no group write protection possible."
),
wp_grp_size(r, 32, 7, "The size of a write protected group."),
sector_size(r, 39, 7, "The size of an erasable sector."),
erase_blk_en(
r,
46,
1,
"Defines the granularity of the unit size of the data to be erased."
),
c_size_mult(
r,
47,
3,
"This parameter is used for coding a factor `MULT` for computing the total \
device size (see `c_size`). The factor `MULT` is defined as \
2<sup>`C_SIZE_MULT+2`</sup>."
),
vdd_w_curr_max(
r,
50,
3,
"The maximum value for write current at the maximal power supply \
V<sub>DD</sub>."
),
vdd_w_curr_min(
r,
53,
3,
"The minimum value for write current at the minimal power supply \
V<sub>DD</sub>."
),
vdd_r_curr_max(
r,
56,
3,
"The maximum value for read current at the maximal power supply \
V<sub>DD</sub>."
),
vdd_r_curr_min(
r,
59,
3,
"The minimum value for read current at the minimal power supply \
V<sub>DD</sub>."
),
c_size(
r,
62,
12,
"This parameter is used to compute the user's data card capacity (not \
include the security protected area)."
),
dsr_imp(
r,
76,
1,
"Defines if the configurable driver stage is integrated on the card."
),
read_blk_misalign(
r,
77,
1,
"Defines if the data block to be read by one command can be spread over \
more than one physical block of the memory device."
),
write_blk_misalign(
r,
78,
1,
"Defines if the data block to be written by one command can be spread over \
more than one physical block of the memory device."
),
read_bl_partial(
r,
79,
1,
"Partial Block Read is always allowed in an SD Memory Card."
),
read_bl_len(
r,
80,
4,
"The maximum read data block length is computed as \
2<sup>`READ_BL_LEN`</sup>."
),
ccc(
r,
84,
12,
"The SD Memory Card command set is divided into subsets (command classes)."
),
tran_speed(r, 96, 8, "The maximum data transfer rate."),
nsac(
r,
104,
8,
"Defines the worst case for the clock-dependent factor of the data access \
time."
),
taac(
r,
112,
8,
"Defines the asynchronous part of the data access time."
)
)]
pub struct Csd10(u128);
#[derive(Clone, Copy, Bitfield)]
#[bitfield(
crc(
rw,
1,
7,
"The CRC field carries the check sum for the CSD contents."
),
file_format(r, 10, 2, "Indicates the file format on the card."),
tmp_write_protect(
rw,
12,
1,
"Temporarily protects the entire card content from being overwritten or \
erased (all write and erase commands for this card are temporarily \
disabled)."
),
perm_write_protect(
rw,
13,
1,
"Permanently protects the entire card content against overwriting or \
erasing (all write and erase commands for this card are permanently \
disabled)."
),
copy(
rw,
14,
1,
"Defines whether the contents is original (`0`) or has been copied (`1`)."
),
file_format_grp(r, 15, 1, "Indicates the selected group of file formats."),
write_bl_partial(
r,
21,
1,
"Defines whether partial block sizes can be used in block write commands."
),
write_bl_len(
r,
22,
4,
"The maximum write data block length is computed as \
2<sup>`WRITE_BL_LEN`</sup>."
),
r2w_factor(
r,
26,
3,
"Defines the typical block program time as a multiple of the read access \
time."
),
wp_grp_enable(
r,
31,
1,
"A value of 0 means no group write protection possible."
),
wp_grp_size(r, 32, 7, "The size of a write protected group."),
sector_size(r, 39, 7, "The size of an erasable sector."),
erase_blk_en(
r,
46,
1,
"Defines the granularity of the unit size of the data to be erased."
),
c_size(
r,
48,
22,
"This parameter is used to calculate the user data area capacity in the SD \
memory card (not include the protected area)."
),
dsr_imp(
r,
76,
1,
"Defines if the configurable driver stage is integrated on the card."
),
read_blk_misalign(
r,
77,
1,
"Defines if the data block to be read by one command can be spread over \
more than one physical block of the memory device."
),
write_blk_misalign(
r,
78,
1,
"Defines if the data block to be written by one command can be spread over \
more than one physical block of the memory device."
),
read_bl_partial(
r,
79,
1,
"Partial Block Read is always allowed in an SD Memory Card."
),
read_bl_len(
r,
80,
4,
"The maximum read data block length is computed as \
2<sup>`READ_BL_LEN`</sup>."
),
ccc(
r,
84,
12,
"The SD Memory Card command set is divided into subsets (command classes)."
),
tran_speed(r, 96, 8, "The maximum data transfer rate."),
nsac(
r,
104,
8,
"Defines the worst case for the clock-dependent factor of the data access \
time."
),
taac(
r,
112,
8,
"Defines the asynchronous part of the data access time."
)
)]
pub struct Csd20(u128);
#[derive(Clone, Copy)]
pub union Csd {
bits: u128,
csd10: Csd10,
csd20: Csd20,
}
#[derive(Clone, Copy, Bitfield)]
#[bitfield(
in_idle_state(
r,
0,
"The card is in idle state and running the initializing process."
),
erase_reset(
r,
1,
"An erase sequence was cleared before executing because an out of erase \
sequence command was received."
),
illegal_command(r, 2, "An illegal command code was detected."),
com_crc_error(r, 3, "The CRC check of the last command failed."),
erase_sequence_error(
r, 4, "An error in the sequence of erase commands occurred."
),
address_error(
r,
5,
"A misaligned address that did not match the block length was used in the \
command."
),
parameter_error(
r,
6,
"The command's argument (e.g. address, block length) was outside the \
allowed range for this card."
)
)]
pub struct R1(u8);
#[derive(Clone, Copy, Bitfield)]
#[bitfield(
card_is_locked(
r,
0,
"Set when the card is locked by the user. Reset when it is unlocked."
),
wp_erase_skip_or_lck_failed(
r,
1,
"This status bit has two functions overloaded. It is set when the host \
attempts to erase a write-protected sector or makes a sequence or \
password errors during card lock/unlock operation."
),
error(
r,
2,
"A general or an unknown error occurred during the operation."
),
cc_error(r, 3, "Internal card controller error."),
card_ecc_failed(
r,
4,
"Card internal ECC was applied but failed to correct the data."
),
wp_violation(r, 5, "The command tried to write a write-protected block."),
erase_param(r, 6, "An invalid selection for erase, sectors or groups."),
out_of_range_or_csd_overwrite(
r,
7,
"Can be either of the following errors:\n\n*The command argument was out \
of the allowed range for this card.\n*The read only section of the CSD \
does not match the card content.\n*An attempt to reverse the copy (set as \
original) or permanent WP (unprotected) bits was made."
)
)]
pub struct Status(u8);
#[derive(Clone, Copy, Bitfield)]
#[bitfield(
check_pattern(r, 0, 8, "Echo back of check pattern in argument."),
voltage_accepted(r, 8, 4, "The card operating voltage."),
command_version(r, 28, 4, "Command version.")
)]
pub struct Cond(u32);
#[derive(Clone, Copy, Bitfield)]
#[bitfield(
error(
r,
0,
"A general or an unknown error occurred during the operation."
),
cc_error(r, 1, "Internal card controller error."),
card_ecc_failed(
r,
2,
"Card internal ECC was applied but failed to correct the data."
),
out_of_range(
r,
3,
"The command argument was out of the allowed range for this card."
)
)]
pub struct ReadResponse(u8);
#[derive(Clone, Copy)]
pub struct WriteResponse(u8);
impl Ocr {
#[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
pub(crate) fn from_buf(buf: &[u8]) -> Self {
assert!(buf.len() >= 4);
unsafe { Ocr(u32::from_be(*(buf.as_ptr() as *const u32))) }
}
pub fn new<F: FnOnce(&mut Self) -> &mut Self>(f: F) -> Self {
let mut ocr = Ocr(0);
f(&mut ocr);
ocr
}
pub fn supports(self, other: Self) -> bool {
self.0 | other.0 != 0
}
}
impl Csd {
#[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
pub(crate) fn from_buf(buf: &[u8]) -> Self {
assert!(buf.len() >= 16);
let bits = unsafe { u128::from_be(*(buf.as_ptr() as *const u128)) };
Self { bits }
}
pub fn csd10(self) -> Option<Csd10> {
unsafe {
if (self.bits >> 126) as u8 == 0 {
Some(self.csd10)
} else {
None
}
}
}
pub fn csd20(self) -> Option<Csd20> {
unsafe {
if (self.bits >> 126) as u8 == 1 {
Some(self.csd20)
} else {
None
}
}
}
}
impl Csd10 {
#[inline(always)]
pub fn sec_count(self) -> u32 {
(self.c_size() as u32 + 1) << (self.c_size_mult() as u32 + 2)
}
#[inline(always)]
pub fn sec_size(self) -> u16 {
1 << self.read_bl_len() as u16
}
#[inline(always)]
pub fn erase_size(self) -> u32 {
(self.sector_size() as u32 + 1) << self.write_bl_len() as u32
}
}
impl Csd20 {
#[inline(always)]
pub fn sec_count(self) -> u32 {
(self.c_size() as u32 + 1) << (CSD20_C_MULT + 2)
}
#[inline(always)]
pub const fn sec_size() -> u16 {
1 << CSD20_READ_BL_LEN
}
#[inline(always)]
pub const fn erase_size() -> u32 {
(CSD20_SECTOR_SIZE + 1) << CSD20_WRITE_BL_LEN
}
}
impl R1 {
pub fn try_from(value: u8) -> Option<Self> {
if value >> 7 == 0 {
Some(R1(value))
} else {
None
}
}
pub fn errck(self, can_precede: bool) -> Result<(), R1Error> {
if self.illegal_command() {
return Err(R1Error::Illegal);
}
if self.com_crc_error() {
return Err(R1Error::Crc);
}
if !can_precede {
if self.erase_sequence_error() {
return Err(R1Error::InvalidEraseSequence);
}
if self.address_error() {
return Err(R1Error::Address);
}
if self.parameter_error() {
return Err(R1Error::Parameter);
}
}
Ok(())
}
}
impl Status {
pub fn new(value: u8) -> Self {
Status(value)
}
pub fn errck(self) -> Result<(), WriteError> {
if self.card_is_locked() {
return Err(WriteError::Locked);
}
if self.wp_erase_skip_or_lck_failed() {
return Err(WriteError::WriteProtectEraseOrLockFailed);
}
if self.error() {
return Err(WriteError::Unknown);
}
if self.cc_error() {
return Err(WriteError::Card);
}
if self.card_ecc_failed() {
return Err(WriteError::Ecc);
}
if self.wp_violation() {
return Err(WriteError::WriteProtect);
}
if self.erase_param() {
return Err(WriteError::EraseParam);
}
if self.out_of_range_or_csd_overwrite() {
return Err(WriteError::OutOfRangeOrCsdOverwrite);
}
Ok(())
}
}
impl Cond {
#[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
pub(crate) fn from_buf(buf: &[u8]) -> Self {
assert!(buf.len() >= 4);
unsafe { Cond(u32::from_be(*(buf.as_ptr() as *const u32))) }
}
}
impl ReadResponse {
pub fn try_from(value: u8) -> Option<Self> {
let value = ReadResponse(value);
if value.is_ok() || value.is_err() {
Some(value)
} else {
None
}
}
pub fn errck(self) -> Result<(), ReadError> {
if self.is_err() {
if self.error() {
return Err(ReadError::Unknown);
}
if self.cc_error() {
return Err(ReadError::Card);
}
if self.card_ecc_failed() {
return Err(ReadError::Ecc);
}
if self.out_of_range() {
return Err(ReadError::OutOfRange);
}
}
Ok(())
}
pub fn is_err(self) -> bool {
self.bits() >> 4 == 0
}
pub fn is_ok(self) -> bool {
self.bits() == BLOCK_START
}
}
impl WriteResponse {
pub fn errck(self) -> Result<(), WriteError> {
match self.0 >> 1 & 0b1110 {
0b010 => Ok(()),
0b101 => Err(WriteError::Crc),
_ => Err(WriteError::Unknown),
}
}
}