use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt;
use crate::core::device::{Device, DeviceClass, PropertySpec, RealizeCtx, ResetKind};
use crate::core::error::{Error, Result};
use crate::core::props::{Props, ValueKind};
use crate::core::space::RamStore;
use crate::core::state::{ChunkReader, ChunkWriter, Sink, Source};
use crate::core::sync::{LockRank, Mutex};
use crate::machine::realize::Instance;
use crate::machine::validate::{ClassSchema, PropSchema};
pub const CLASS_NAME: &str = "sd.card";
const STATE_VERSION: u32 = 1;
pub const BLOCK: u64 = 512;
pub const MAX_STANDARD_CAPACITY: u64 = 2 * 1024 * 1024 * 1024;
pub const HIGH_CAPACITY_UNIT: u64 = 512 * 1024;
pub const OUT_OF_RANGE: u32 = 1 << 31;
pub const ADDRESS_ERROR: u32 = 1 << 30;
pub const BLOCK_LEN_ERROR: u32 = 1 << 29;
pub const ERASE_SEQ_ERROR: u32 = 1 << 28;
pub const ERASE_PARAM: u32 = 1 << 27;
pub const WP_VIOLATION: u32 = 1 << 26;
pub const ILLEGAL_COMMAND: u32 = 1 << 22;
pub const CARD_ERROR: u32 = 1 << 19;
pub const READY_FOR_DATA: u32 = 1 << 8;
pub const APP_CMD: u32 = 1 << 5;
pub const AKE_SEQ_ERROR: u32 = 1 << 3;
const STATE_SHIFT: u32 = 9;
const CLEAR_ON_READ: u32 = OUT_OF_RANGE
| ADDRESS_ERROR
| BLOCK_LEN_ERROR
| ERASE_SEQ_ERROR
| ERASE_PARAM
| WP_VIOLATION
| ILLEGAL_COMMAND
| AKE_SEQ_ERROR;
pub mod cmd {
pub const GO_IDLE_STATE: u8 = 0;
pub const ALL_SEND_CID: u8 = 2;
pub const SEND_RELATIVE_ADDR: u8 = 3;
pub const SWITCH_FUNC: u8 = 6;
pub const SELECT_CARD: u8 = 7;
pub const SEND_IF_COND: u8 = 8;
pub const SEND_CSD: u8 = 9;
pub const SEND_CID: u8 = 10;
pub const STOP_TRANSMISSION: u8 = 12;
pub const SEND_STATUS: u8 = 13;
pub const GO_INACTIVE_STATE: u8 = 15;
pub const SET_BLOCKLEN: u8 = 16;
pub const READ_SINGLE_BLOCK: u8 = 17;
pub const READ_MULTIPLE_BLOCK: u8 = 18;
pub const SET_BLOCK_COUNT: u8 = 23;
pub const WRITE_BLOCK: u8 = 24;
pub const WRITE_MULTIPLE_BLOCK: u8 = 25;
pub const ERASE_WR_BLK_START: u8 = 32;
pub const ERASE_WR_BLK_END: u8 = 33;
pub const ERASE: u8 = 38;
pub const APP_CMD: u8 = 55;
pub const A_SET_BUS_WIDTH: u8 = 6;
pub const A_SD_STATUS: u8 = 13;
pub const A_SD_SEND_OP_COND: u8 = 41;
pub const A_SEND_SCR: u8 = 51;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
#[repr(u8)]
pub enum Phase {
#[default]
Idle = 0,
Ready = 1,
Identification = 2,
Standby = 3,
Transfer = 4,
SendingData = 5,
ReceiveData = 6,
Programming = 7,
Disconnect = 8,
Inactive = 15,
}
impl Phase {
#[must_use]
pub fn code(self) -> u32 {
u32::from(self as u8) & 0xf
}
fn from_code(code: u8) -> Result<Phase> {
Ok(match code {
0 => Phase::Idle,
1 => Phase::Ready,
2 => Phase::Identification,
3 => Phase::Standby,
4 => Phase::Transfer,
5 => Phase::SendingData,
6 => Phase::ReceiveData,
7 => Phase::Programming,
8 => Phase::Disconnect,
15 => Phase::Inactive,
other => {
return Err(Error::State(format!("{other} is not an SD card state")));
}
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BusMode {
#[default]
Sd,
Spi,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Reply {
None,
Short {
index: u8,
value: u32,
busy: bool,
},
Long([u32; 4]),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[must_use]
pub enum Data {
Moved,
Ended,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Transfer {
to_host: bool,
payload: Option<Vec<u8>>,
addr: u64,
done: u32,
len: u32,
multiple: bool,
left: Option<u32>,
buf: Vec<u8>,
}
impl Transfer {
fn payload(bytes: Vec<u8>) -> Transfer {
let len = bytes.len() as u32;
Transfer {
to_host: true,
payload: Some(bytes),
addr: 0,
done: 0,
len,
multiple: false,
left: None,
buf: Vec::new(),
}
}
}
#[derive(Debug)]
struct Volatile {
phase: Phase,
rca: u16,
next_rca: u16,
block_len: u32,
app_cmd: bool,
sticky: u32,
bus_width: u8,
access_mode: u8,
block_count: Option<u32>,
erase_start: Option<u32>,
erase_end: Option<u32>,
transfer: Option<Transfer>,
}
impl Volatile {
fn power_on(next_rca: u16) -> Volatile {
Volatile {
phase: Phase::Idle,
rca: 0,
next_rca,
block_len: BLOCK as u32,
app_cmd: false,
sticky: 0,
bus_width: 1,
access_mode: 0,
block_count: None,
erase_start: None,
erase_end: None,
transfer: None,
}
}
}
#[derive(Debug, Clone)]
pub struct Identity {
pub capacity: u64,
pub high_capacity: bool,
pub read_only: bool,
pub cid: [u8; 16],
pub csd: [u8; 16],
pub scr: [u8; 8],
}
impl Identity {
pub fn new(
capacity: u64,
high_capacity: bool,
read_only: bool,
text: IdentityText<'_>,
) -> Result<Identity> {
if capacity == 0 || !capacity.is_multiple_of(BLOCK) {
return Err(config(format!(
"a card holds a whole number of {BLOCK}-byte blocks, and {capacity} is not one"
)));
}
let csd = if high_capacity {
csd_v2(capacity, read_only)?
} else {
csd_v1(capacity, read_only)?
};
Ok(Identity {
capacity,
high_capacity,
read_only,
cid: cid(text),
csd,
scr: scr(),
})
}
#[must_use]
pub fn blocks(&self) -> u64 {
self.capacity / BLOCK
}
}
pub struct SdCard {
id: Identity,
mode: BusMode,
media: Arc<RamStore>,
state: Mutex<Volatile>,
first_rca: u16,
}
impl fmt::Debug for SdCard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SdCard")
.field("capacity", &self.id.capacity)
.field("high_capacity", &self.id.high_capacity)
.field("read_only", &self.id.read_only)
.field("mode", &self.mode)
.finish_non_exhaustive()
}
}
impl SdCard {
pub fn new(props: &Props) -> Result<SdCard> {
let mut r = props.reader();
let capacity = r.require_size("size")?;
let high_capacity = r.or("high-capacity", capacity > MAX_STANDARD_CAPACITY)?;
let read_only = r.or("readonly", false)?;
let manufacturer = r.or_range("manufacturer", 0x03u64, 0..=0xff)? as u8;
let oem = r.or_str("oem", "RE")?.to_string();
let product = r.or_str("product", "RSEMU")?.to_string();
let revision = r.or_range("revision", 0x10u64, 0..=0xff)? as u8;
let serial = r.or_range("serial", 1u64, 0..=0xffff_ffff)? as u32;
let year = r.or_range("year", 2024u64, 2000..=2255)? as u16;
let month = r.or_range("month", 1u64, 1..=12)? as u8;
let rca = r.or_range("rca", 1u64, 1..=0xffff)? as u16;
let mode = match r.or_enum("mode", "sd", &["sd", "spi"])? {
"spi" => BusMode::Spi,
_ => BusMode::Sd,
};
let _ = r.optional_str("slot")?;
let image = r
.optional_media("image")?
.map(crate::core::props::Media::to_bytes);
r.finish()?;
let id = Identity::new(
capacity,
high_capacity,
read_only,
IdentityText {
manufacturer,
oem: &oem,
product: &product,
revision,
serial,
year,
month,
},
)?;
let card = SdCard::with_identity(id, mode, rca)?;
if let Some(image) = image {
if image.len() as u64 > capacity {
return Err(config(format!(
"the bound image is {} byte(s) and the card holds {capacity}",
image.len()
)));
}
card.load_image(0, &image)?;
}
Ok(card)
}
pub fn with_identity(id: Identity, mode: BusMode, first_rca: u16) -> Result<SdCard> {
if usize::try_from(id.capacity).is_err() {
return Err(config(format!(
"a card of {} byte(s) is larger than this host's address space",
id.capacity
)));
}
let media = Arc::new(RamStore::new(id.capacity));
Ok(SdCard {
id,
mode,
media,
state: Mutex::with_rank(LockRank::DEVICE, Volatile::power_on(first_rca)),
first_rca,
})
}
#[must_use]
pub fn identity(&self) -> &Identity {
&self.id
}
#[must_use]
pub fn bus_mode(&self) -> BusMode {
self.mode
}
#[must_use]
pub fn phase(&self) -> Phase {
self.state.lock().phase
}
#[must_use]
pub fn rca(&self) -> u16 {
self.state.lock().rca
}
#[must_use]
pub fn bus_width(&self) -> u8 {
self.state.lock().bus_width
}
#[must_use]
pub fn peek_status(&self) -> u32 {
let state = self.state.lock();
Self::status_of(&state)
}
#[must_use]
pub fn is_busy(&self) -> bool {
false
}
pub fn read_media(&self, offset: u64, dst: &mut [u8]) -> Result<()> {
self.media
.read_at(offset, dst)
.map_err(|_| Error::State(format!("{offset:#x} is outside this card")))
}
pub fn write_media(&self, offset: u64, src: &[u8]) -> Result<()> {
self.media
.write_at(offset, src)
.map_err(|_| Error::State(format!("{offset:#x} is outside this card")))
}
pub fn load_image(&self, offset: u64, bytes: &[u8]) -> Result<()> {
self.media.write_at(offset, bytes).map_err(|_| {
config(format!(
"an image of {} byte(s) at {offset:#x} does not fit in a card of {}",
bytes.len(),
self.id.capacity
))
})
}
#[must_use]
pub fn contents(&self) -> Vec<u8> {
let mut out = alloc::vec![0u8; self.id.capacity as usize];
let _ = self.media.read_at(0, &mut out);
out
}
pub fn power_cycle(&self) {
*self.state.lock() = Volatile::power_on(self.first_rca);
}
pub fn command(&self, index: u8, arg: u32) -> Reply {
let mut state = self.state.lock();
if state.phase == Phase::Inactive {
return Reply::None;
}
let app = state.app_cmd;
state.app_cmd = false;
if app {
self.app_command(&mut state, index, arg)
} else {
self.basic_command(&mut state, index, arg)
}
}
pub fn read_data(&self, dst: &mut [u8]) -> Data {
let mut state = self.state.lock();
let mut at = 0usize;
while at < dst.len() {
match state.transfer.as_ref() {
Some(t) if t.to_host => {}
_ => return Data::Ended,
}
if state.transfer.as_ref().is_some_and(|t| t.done == t.len)
&& !Self::next_block(&mut state)
{
return Data::Ended;
}
let Some(t) = state.transfer.as_mut() else {
return Data::Ended;
};
let run = ((t.len - t.done) as usize).min(dst.len() - at);
let ok = match &t.payload {
Some(bytes) => {
let from = t.done as usize;
dst[at..at + run].copy_from_slice(&bytes[from..from + run]);
true
}
None => self
.media
.read_at(t.addr + u64::from(t.done), &mut dst[at..at + run])
.is_ok(),
};
if !ok {
state.sticky |= OUT_OF_RANGE;
state.transfer = None;
state.phase = Phase::Transfer;
return Data::Ended;
}
let t = state.transfer.as_mut().expect("still in flight");
t.done += run as u32;
at += run;
}
Self::settle_after_read(&mut state);
Data::Moved
}
pub fn write_data(&self, src: &[u8]) -> Data {
let mut state = self.state.lock();
let mut at = 0usize;
while at < src.len() {
match state.transfer.as_ref() {
Some(t) if !t.to_host => {}
_ => return Data::Ended,
}
if state.transfer.as_ref().is_some_and(|t| t.done == t.len)
&& !Self::next_block(&mut state)
{
return Data::Ended;
}
let Some(t) = state.transfer.as_mut() else {
return Data::Ended;
};
let run = ((t.len as usize) - t.buf.len()).min(src.len() - at);
t.buf.extend_from_slice(&src[at..at + run]);
at += run;
if t.buf.len() as u32 == t.len {
let addr = t.addr;
let block = core::mem::take(&mut t.buf);
t.done = t.len;
if !self.program(&mut state, addr, &block) {
return Data::Ended;
}
Self::settle_after_write(&mut state);
}
}
Data::Moved
}
pub fn abort(&self) {
let mut state = self.state.lock();
state.transfer = None;
if matches!(
state.phase,
Phase::SendingData | Phase::ReceiveData | Phase::Programming
) {
state.phase = Phase::Transfer;
}
}
fn basic_command(&self, state: &mut Volatile, index: u8, arg: u32) -> Reply {
match index {
cmd::GO_IDLE_STATE => {
let rca = state.next_rca;
*state = Volatile::power_on(rca);
Reply::None
}
cmd::ALL_SEND_CID => {
if state.phase != Phase::Ready {
return Self::illegal(state, index);
}
state.phase = Phase::Identification;
Reply::Long(words_of(&self.id.cid))
}
cmd::SEND_RELATIVE_ADDR => {
if !matches!(state.phase, Phase::Identification | Phase::Standby) {
return Self::illegal(state, index);
}
let status = Self::take_status(state);
state.rca = state.next_rca;
state.next_rca = state.next_rca.wrapping_add(1).max(1);
state.phase = Phase::Standby;
Reply::Short {
index,
value: (u32::from(state.rca) << 16) | r6_status(status),
busy: false,
}
}
cmd::SWITCH_FUNC => {
if state.phase != Phase::Transfer {
return Self::illegal(state, index);
}
let payload = Self::switch_status(state, arg);
let status = Self::take_status(state);
state.transfer = Some(Transfer::payload(payload));
state.phase = Phase::SendingData;
Reply::Short {
index,
value: status,
busy: false,
}
}
cmd::SELECT_CARD => {
let target = (arg >> 16) as u16;
if target != state.rca || state.rca == 0 {
if matches!(state.phase, Phase::Transfer | Phase::Programming) {
state.phase = Phase::Standby;
}
return Reply::None;
}
let status = Self::take_status(state);
match state.phase {
Phase::Standby => state.phase = Phase::Transfer,
Phase::Disconnect => state.phase = Phase::Programming,
_ => {}
}
Reply::Short {
index,
value: status,
busy: true,
}
}
cmd::SEND_IF_COND => {
if state.phase != Phase::Idle {
return Self::illegal(state, index);
}
let vhs = (arg >> 8) & 0xf;
if vhs != 0x1 {
return Reply::None;
}
Reply::Short {
index,
value: arg & 0xfff,
busy: false,
}
}
cmd::SEND_CSD | cmd::SEND_CID => {
let target = (arg >> 16) as u16;
if state.phase != Phase::Standby || target != state.rca {
return Reply::None;
}
let register = if index == cmd::SEND_CSD {
&self.id.csd
} else {
&self.id.cid
};
Reply::Long(words_of(register))
}
cmd::STOP_TRANSMISSION => {
if !matches!(
state.phase,
Phase::SendingData | Phase::ReceiveData | Phase::Programming
) {
return Self::illegal(state, index);
}
let status = Self::take_status(state);
state.transfer = None;
state.phase = Phase::Transfer;
Reply::Short {
index,
value: status,
busy: true,
}
}
cmd::SEND_STATUS => {
let target = (arg >> 16) as u16;
if self.mode == BusMode::Sd && (target != state.rca || state.rca == 0) {
return Reply::None;
}
Reply::Short {
index,
value: Self::take_status(state),
busy: false,
}
}
cmd::GO_INACTIVE_STATE => {
let target = (arg >> 16) as u16;
if target == state.rca && state.rca != 0 {
state.phase = Phase::Inactive;
}
Reply::None
}
cmd::SET_BLOCKLEN => {
if state.phase != Phase::Transfer {
return Self::illegal(state, index);
}
let ok = if self.id.high_capacity {
arg == BLOCK as u32
} else {
arg >= 1 && u64::from(arg) <= BLOCK
};
if ok {
state.block_len = arg;
} else {
state.sticky |= BLOCK_LEN_ERROR;
}
Reply::Short {
index,
value: Self::take_status(state),
busy: false,
}
}
cmd::READ_SINGLE_BLOCK | cmd::READ_MULTIPLE_BLOCK => {
self.start_media_transfer(state, index, arg, true)
}
cmd::SET_BLOCK_COUNT => {
if state.phase != Phase::Transfer {
return Self::illegal(state, index);
}
state.block_count = Some(arg);
Reply::Short {
index,
value: Self::take_status(state),
busy: false,
}
}
cmd::WRITE_BLOCK | cmd::WRITE_MULTIPLE_BLOCK => {
self.start_media_transfer(state, index, arg, false)
}
cmd::ERASE_WR_BLK_START | cmd::ERASE_WR_BLK_END => {
if state.phase != Phase::Transfer {
return Self::illegal(state, index);
}
if index == cmd::ERASE_WR_BLK_START {
state.erase_start = Some(arg);
state.erase_end = None;
} else if state.erase_start.is_some() {
state.erase_end = Some(arg);
} else {
state.sticky |= ERASE_SEQ_ERROR;
}
Reply::Short {
index,
value: Self::take_status(state),
busy: false,
}
}
cmd::ERASE => {
if state.phase != Phase::Transfer {
return Self::illegal(state, index);
}
self.erase(state);
Reply::Short {
index,
value: Self::take_status(state),
busy: true,
}
}
cmd::APP_CMD => {
let target = (arg >> 16) as u16;
let addressed = match self.mode {
BusMode::Sd if state.rca != 0 => target == state.rca,
BusMode::Sd => target == 0,
BusMode::Spi => true,
};
if !addressed {
return Reply::None;
}
state.app_cmd = true;
let value = Self::take_status(state) | APP_CMD;
Reply::Short {
index,
value,
busy: false,
}
}
_ => Self::illegal(state, index),
}
}
fn app_command(&self, state: &mut Volatile, index: u8, arg: u32) -> Reply {
match index {
cmd::A_SD_SEND_OP_COND => {
if state.phase != Phase::Idle {
return Self::illegal(state, index);
}
let window = arg & 0x00ff_8000;
if window == 0 {
return Reply::Short {
index: 0x3f,
value: self.ocr(false),
busy: false,
};
}
if self.id.high_capacity && arg & (1 << 30) == 0 {
state.phase = Phase::Inactive;
return Reply::None;
}
state.phase = match self.mode {
BusMode::Sd => Phase::Ready,
BusMode::Spi => Phase::Transfer,
};
Reply::Short {
index: 0x3f,
value: self.ocr(true),
busy: false,
}
}
cmd::A_SET_BUS_WIDTH => {
if state.phase != Phase::Transfer {
return Self::illegal(state, index);
}
match arg & 0x3 {
0b00 => state.bus_width = 1,
0b10 => state.bus_width = 4,
_ => state.sticky |= OUT_OF_RANGE,
}
Reply::Short {
index,
value: Self::take_status(state) | APP_CMD,
busy: false,
}
}
cmd::A_SD_STATUS => {
if state.phase != Phase::Transfer {
return Self::illegal(state, index);
}
let payload = Self::sd_status(state);
let value = Self::take_status(state) | APP_CMD;
state.transfer = Some(Transfer::payload(payload));
state.phase = Phase::SendingData;
Reply::Short {
index,
value,
busy: false,
}
}
cmd::A_SEND_SCR => {
if state.phase != Phase::Transfer {
return Self::illegal(state, index);
}
let value = Self::take_status(state) | APP_CMD;
state.transfer = Some(Transfer::payload(self.id.scr.to_vec()));
state.phase = Phase::SendingData;
Reply::Short {
index,
value,
busy: false,
}
}
_ => self.basic_command(state, index, arg),
}
}
fn start_media_transfer(
&self,
state: &mut Volatile,
index: u8,
arg: u32,
to_host: bool,
) -> Reply {
if state.phase != Phase::Transfer {
return Self::illegal(state, index);
}
let multiple = index == cmd::READ_MULTIPLE_BLOCK || index == cmd::WRITE_MULTIPLE_BLOCK;
let addr = if self.id.high_capacity {
u64::from(arg) * BLOCK
} else {
u64::from(arg)
};
let len = if to_host {
state.block_len
} else {
BLOCK as u32
};
let refuse = |state: &mut Volatile, bit: u32| -> Reply {
state.sticky |= bit;
Reply::Short {
index,
value: Self::take_status(state),
busy: false,
}
};
if !to_host && u64::from(state.block_len) != BLOCK {
return refuse(state, BLOCK_LEN_ERROR);
}
if addr >= self.id.capacity || addr + u64::from(len) > self.id.capacity {
return refuse(state, OUT_OF_RANGE);
}
if addr / BLOCK != (addr + u64::from(len) - 1) / BLOCK {
return refuse(state, ADDRESS_ERROR);
}
if !to_host && self.id.read_only {
return refuse(state, WP_VIOLATION);
}
let left = if multiple {
state.block_count.take().filter(|n| *n != 0).map(|n| n - 1)
} else {
state.block_count = None;
None
};
let value = Self::take_status(state);
state.transfer = Some(Transfer {
to_host,
payload: None,
addr,
done: 0,
len,
multiple,
left,
buf: Vec::new(),
});
state.phase = if to_host {
Phase::SendingData
} else {
Phase::ReceiveData
};
Reply::Short {
index,
value,
busy: false,
}
}
fn next_block(state: &mut Volatile) -> bool {
let Some(t) = state.transfer.as_mut() else {
return false;
};
let more = t.multiple && t.payload.is_none() && t.left != Some(0);
if !more {
state.transfer = None;
state.phase = Phase::Transfer;
return false;
}
t.addr += u64::from(t.len);
t.done = 0;
t.buf.clear();
if let Some(left) = t.left.as_mut() {
*left -= 1;
}
true
}
fn settle_after_read(state: &mut Volatile) {
let Some(t) = state.transfer.as_ref() else {
return;
};
if t.done < t.len {
return;
}
if !t.multiple || t.left == Some(0) {
state.transfer = None;
state.phase = Phase::Transfer;
}
}
fn settle_after_write(state: &mut Volatile) {
let Some(t) = state.transfer.as_ref() else {
return;
};
if !t.multiple || t.left == Some(0) {
state.transfer = None;
state.phase = Phase::Transfer;
}
}
fn program(&self, state: &mut Volatile, addr: u64, block: &[u8]) -> bool {
let fail = |state: &mut Volatile, bit: u32| {
state.sticky |= bit;
state.transfer = None;
state.phase = Phase::Transfer;
false
};
if self.id.read_only {
return fail(state, WP_VIOLATION);
}
if self.media.write_at(addr, block).is_err() {
return fail(state, OUT_OF_RANGE);
}
true
}
fn erase(&self, state: &mut Volatile) {
let (Some(start), Some(end)) = (state.erase_start, state.erase_end) else {
state.sticky |= ERASE_SEQ_ERROR;
return;
};
state.erase_start = None;
state.erase_end = None;
if self.id.read_only {
state.sticky |= WP_VIOLATION;
return;
}
let unit = if self.id.high_capacity { BLOCK } else { 1 };
let from = u64::from(start) * unit;
let to = u64::from(end) * unit + BLOCK;
if from > to || to > self.id.capacity {
state.sticky |= ERASE_PARAM;
return;
}
if self.media.fill(from, to - from, 0).is_err() {
state.sticky |= ERASE_PARAM;
}
}
fn illegal(state: &mut Volatile, index: u8) -> Reply {
state.sticky |= ILLEGAL_COMMAND;
let value = Self::take_status(state);
Reply::Short {
index,
value,
busy: false,
}
}
fn status_of(state: &Volatile) -> u32 {
let mut status = state.sticky | (state.phase.code() << STATE_SHIFT) | READY_FOR_DATA;
if state.sticky & CLEAR_ON_READ != 0 {
status |= CARD_ERROR;
}
if state.app_cmd {
status |= APP_CMD;
}
status
}
fn take_status(state: &mut Volatile) -> u32 {
let status = Self::status_of(state);
state.sticky &= !CLEAR_ON_READ;
status
}
fn ocr(&self, ready: bool) -> u32 {
let mut ocr = 0x00ff_8000;
if ready {
ocr |= 1 << 31;
if self.id.high_capacity {
ocr |= 1 << 30;
}
}
ocr
}
fn switch_status(state: &mut Volatile, arg: u32) -> Vec<u8> {
let mut out = alloc::vec![0u8; 64];
out[0] = 0x00;
out[1] = 0x64;
for group in 0..6usize {
let support: u16 = if group == 5 { 0x0003 } else { 0x0001 };
out[2 + group * 2] = (support >> 8) as u8;
out[3 + group * 2] = support as u8;
}
let switching = arg & (1 << 31) != 0;
let mut selected = [0xfu8; 6];
for (group, slot) in selected.iter_mut().enumerate() {
let want = ((arg >> (group * 4)) & 0xf) as u8;
let support: u16 = if group == 0 { 0x0003 } else { 0x0001 };
*slot = if want == 0xf {
if group == 0 { state.access_mode } else { 0 }
} else if support & (1u16 << want) != 0 {
if switching && group == 0 {
state.access_mode = want;
}
want
} else {
0xf
};
}
out[14] = (selected[5] << 4) | selected[4];
out[15] = (selected[3] << 4) | selected[2];
out[16] = (selected[1] << 4) | selected[0];
out[17] = 0x01;
out
}
fn sd_status(state: &Volatile) -> Vec<u8> {
let mut out = alloc::vec![0u8; 64];
out[0] = if state.bus_width == 4 { 0b10 << 6 } else { 0 };
out[8] = 0x04;
out[9] = 0xff;
out[10] = 0x90;
out
}
fn save(&self, w: &mut ChunkWriter<'_>) -> Result<()> {
w.write_bytes(&self.contents())?;
let state = self.state.lock();
w.write_u8(state.phase as u8)?;
w.write_u16(state.rca)?;
w.write_u16(state.next_rca)?;
w.write_u32(state.block_len)?;
w.write_bool(state.app_cmd)?;
w.write_u32(state.sticky)?;
w.write_u8(state.bus_width)?;
w.write_u8(state.access_mode)?;
write_option_u32(w, state.block_count)?;
write_option_u32(w, state.erase_start)?;
write_option_u32(w, state.erase_end)?;
match &state.transfer {
None => w.write_bool(false)?,
Some(t) => {
w.write_bool(true)?;
w.write_bool(t.to_host)?;
match &t.payload {
Some(bytes) => {
w.write_bool(true)?;
w.write_bytes(bytes)?;
}
None => w.write_bool(false)?,
}
w.write_u64(t.addr)?;
w.write_u32(t.done)?;
w.write_u32(t.len)?;
w.write_bool(t.multiple)?;
write_option_u32(w, t.left)?;
w.write_bytes(&t.buf)?;
}
}
Ok(())
}
fn load(&self, r: &mut ChunkReader<'_>) -> Result<()> {
let bytes: &[u8] = r.read_bytes()?;
if bytes.len() as u64 != self.id.capacity {
return Err(Error::State(format!(
"the snapshot holds a card of {} byte(s), this one holds {}",
bytes.len(),
self.id.capacity
)));
}
self.media
.write_at(0, bytes)
.map_err(|_| Error::State(String::from("the card refused the snapshot")))?;
let phase = Phase::from_code(r.read_u8()?)?;
let rca = r.read_u16()?;
let next_rca = r.read_u16()?;
let block_len = r.read_u32()?;
let app_cmd = r.read_bool()?;
let sticky = r.read_u32()?;
let bus_width = r.read_u8()?;
let access_mode = r.read_u8()?;
let block_count = read_option_u32(r)?;
let erase_start = read_option_u32(r)?;
let erase_end = read_option_u32(r)?;
let transfer = if r.read_bool()? {
let to_host = r.read_bool()?;
let payload = if r.read_bool()? {
Some(r.read_bytes()?.to_vec())
} else {
None
};
let addr = r.read_u64()?;
let done = r.read_u32()?;
let len = r.read_u32()?;
let multiple = r.read_bool()?;
let left = read_option_u32(r)?;
let buf = r.read_bytes()?.to_vec();
if len == 0 || u64::from(len) > BLOCK || done > len || buf.len() as u32 > len {
return Err(Error::State(format!(
"a snapshot transfer of {done}/{len} byte(s) is not one this card can hold"
)));
}
Some(Transfer {
to_host,
payload,
addr,
done,
len,
multiple,
left,
buf,
})
} else {
None
};
if block_len == 0 || u64::from(block_len) > BLOCK {
return Err(Error::State(format!(
"{block_len} is not a block length an SD card can hold"
)));
}
if bus_width != 1 && bus_width != 4 {
return Err(Error::State(format!(
"{bus_width} is not an SD data bus width"
)));
}
*self.state.lock() = Volatile {
phase,
rca,
next_rca,
block_len,
app_cmd,
sticky,
bus_width,
access_mode,
block_count,
erase_start,
erase_end,
transfer,
};
Ok(())
}
}
fn write_option_u32(w: &mut ChunkWriter<'_>, value: Option<u32>) -> Result<()> {
match value {
Some(v) => {
w.write_bool(true)?;
w.write_u32(v)
}
None => w.write_bool(false),
}
}
fn read_option_u32(r: &mut ChunkReader<'_>) -> Result<Option<u32>> {
if r.read_bool()? {
Ok(Some(r.read_u32()?))
} else {
Ok(None)
}
}
fn r6_status(status: u32) -> u32 {
(((status >> 23) & 1) << 15)
| (((status >> 22) & 1) << 14)
| (((status >> 19) & 1) << 13)
| (status & 0x1fff)
}
fn words_of(register: &[u8; 16]) -> [u32; 4] {
let mut out = [0u32; 4];
for (i, word) in out.iter_mut().enumerate() {
let b = ®ister[i * 4..i * 4 + 4];
*word = u32::from_be_bytes([b[0], b[1], b[2], b[3]]);
}
out
}
#[must_use]
pub fn crc7(bytes: &[u8]) -> u8 {
let mut crc = 0u8;
for byte in bytes {
let mut b = *byte;
for _ in 0..8 {
let bit = (b & 0x80) >> 7;
b <<= 1;
let top = (crc >> 6) & 1;
crc = (crc << 1) & 0x7f;
if top ^ bit != 0 {
crc ^= 0x09;
}
}
}
crc & 0x7f
}
#[derive(Debug, Clone, Copy)]
pub struct IdentityText<'a> {
pub manufacturer: u8,
pub oem: &'a str,
pub product: &'a str,
pub revision: u8,
pub serial: u32,
pub year: u16,
pub month: u8,
}
fn cid(text: IdentityText<'_>) -> [u8; 16] {
let mut cid = [0u8; 16];
cid[0] = text.manufacturer;
fixed_ascii(&mut cid[1..3], text.oem);
fixed_ascii(&mut cid[3..8], text.product);
cid[8] = text.revision;
cid[9..13].copy_from_slice(&text.serial.to_be_bytes());
let year = text.year.saturating_sub(2000) as u8;
cid[13] = year >> 4;
cid[14] = ((year & 0x0f) << 4) | (text.month & 0x0f);
cid[15] = (crc7(&cid[..15]) << 1) | 1;
cid
}
fn csd_v1(capacity: u64, read_only: bool) -> Result<[u8; 16]> {
if capacity > MAX_STANDARD_CAPACITY {
return Err(config(format!(
"{capacity} byte(s) is past the {MAX_STANDARD_CAPACITY} a standard-capacity CSD can \
describe; set `high-capacity = true`"
)));
}
let blocks = capacity / BLOCK;
let mut mult = 0u32;
while mult < 8 && blocks >> (mult + 2) > 4096 {
mult += 1;
}
let unit = 1u64 << (mult + 2);
if mult == 8 || !blocks.is_multiple_of(unit) || blocks / unit == 0 {
return Err(config(format!(
"{capacity} byte(s) is not a size a standard-capacity CSD can express exactly; a \
multiple of {} would be",
unit * BLOCK
)));
}
let c_size = (blocks / unit - 1) as u32;
let mut csd = [0u8; 16];
csd[0] = 0x00; csd[1] = 0x26; csd[2] = 0x00; csd[3] = 0x32; csd[4] = 0x5b; csd[5] = 0x59; csd[6] = 0x80 | ((c_size >> 10) & 0x03) as u8;
csd[7] = ((c_size >> 2) & 0xff) as u8;
csd[8] = (((c_size & 0x3) as u8) << 6) | 0x3f;
csd[9] = 0xfc | ((mult >> 1) & 0x3) as u8;
csd[10] = (((mult & 1) as u8) << 7) | 0x40 | (0x7f >> 1);
csd[11] = 0x80; csd[12] = 0x0a; csd[13] = 0x40; csd[14] = if read_only { 0x20 } else { 0x00 };
csd[15] = (crc7(&csd[..15]) << 1) | 1;
Ok(csd)
}
fn csd_v2(capacity: u64, read_only: bool) -> Result<[u8; 16]> {
if !capacity.is_multiple_of(HIGH_CAPACITY_UNIT) {
return Err(config(format!(
"a high-capacity card's C_SIZE counts {HIGH_CAPACITY_UNIT}-byte units, and \
{capacity} is not a multiple of one"
)));
}
let units = capacity / HIGH_CAPACITY_UNIT;
if units - 1 > 0x3f_ffff {
return Err(config(format!(
"{capacity} byte(s) is past what a version 2.0 CSD's 22-bit C_SIZE can describe"
)));
}
let c_size = (units - 1) as u32;
let mut csd = [0u8; 16];
csd[0] = 0x40; csd[1] = 0x0e; csd[2] = 0x00; csd[3] = 0x32; csd[4] = 0x5b; csd[5] = 0x59; csd[6] = 0x00; csd[7] = ((c_size >> 16) & 0x3f) as u8;
csd[8] = ((c_size >> 8) & 0xff) as u8;
csd[9] = (c_size & 0xff) as u8;
csd[10] = 0x40 | (0x7f >> 1); csd[11] = 0x80; csd[12] = 0x0a; csd[13] = 0x40; csd[14] = if read_only { 0x20 } else { 0x00 };
csd[15] = (crc7(&csd[..15]) << 1) | 1;
Ok(csd)
}
fn scr() -> [u8; 8] {
let mut scr = [0u8; 8];
scr[0] = 0x02;
scr[1] = 0x05;
scr[2] = 0x80;
scr[3] = 0x02;
scr
}
fn fixed_ascii(out: &mut [u8], text: &str) {
out.fill(b' ');
for (slot, byte) in out.iter_mut().zip(text.bytes()) {
*slot = if byte.is_ascii_graphic() { byte } else { b'?' };
}
}
fn config(message: String) -> Error {
Error::Config {
at: String::from(CLASS_NAME),
message,
}
}
pub static CLASS: DeviceClass = DeviceClass {
name: CLASS_NAME,
version: STATE_VERSION,
summary: "an SD memory card: the command set, the state machine and the registers",
properties: &[
PropertySpec {
name: "size",
kind: ValueKind::Size,
required: true,
summary: "how many bytes the card holds, as in `size = 64M`",
},
PropertySpec {
name: "image",
kind: ValueKind::Media,
required: false,
summary: "the media slot holding the initial contents; the rest reads zero",
},
PropertySpec {
name: "slot",
kind: ValueKind::Str,
required: false,
summary: "the named card slot this card sits in (default `sd0`)",
},
PropertySpec {
name: "high-capacity",
kind: ValueKind::Bool,
required: false,
summary: "SDHC block addressing rather than SDSC byte addressing (default: by size)",
},
PropertySpec {
name: "readonly",
kind: ValueKind::Bool,
required: false,
summary: "the mechanical write-protect tab: a write fails with WP_VIOLATION",
},
PropertySpec {
name: "mode",
kind: ValueKind::Str,
required: false,
summary: "`sd` (the default) or `spi`, which has no bus addressing",
},
PropertySpec {
name: "manufacturer",
kind: ValueKind::Uint,
required: false,
summary: "the CID's MID field",
},
PropertySpec {
name: "oem",
kind: ValueKind::Str,
required: false,
summary: "the CID's two-character OID field",
},
PropertySpec {
name: "product",
kind: ValueKind::Str,
required: false,
summary: "the CID's five-character PNM field",
},
PropertySpec {
name: "revision",
kind: ValueKind::Uint,
required: false,
summary: "the CID's PRV field",
},
PropertySpec {
name: "serial",
kind: ValueKind::Uint,
required: false,
summary: "the CID's PSN field; a constant, because a run must be reproducible",
},
PropertySpec {
name: "year",
kind: ValueKind::Uint,
required: false,
summary: "the CID's manufacturing year, 2000 to 2255",
},
PropertySpec {
name: "month",
kind: ValueKind::Uint,
required: false,
summary: "the CID's manufacturing month, 1 to 12",
},
PropertySpec {
name: "rca",
kind: ValueKind::Uint,
required: false,
summary: "the address the first CMD3 publishes (default 1)",
},
],
construct: |props| Ok(Box::new(CardDevice::new(props)?)),
};
#[derive(Debug)]
pub struct CardDevice {
card: Arc<SdCard>,
slot: String,
}
impl CardDevice {
pub fn new(props: &Props) -> Result<CardDevice> {
let slot = props
.get("slot")
.and_then(crate::core::props::Value::as_str)
.unwrap_or(super::DEFAULT_SLOT)
.to_string();
let card = Arc::new(SdCard::new(props)?);
let holder = super::slots::attach(props, &slot)?;
holder.insert(Arc::clone(&card)).map_err(|_| {
config(format!(
"two cards were put in the slot called `{slot}`; give one of them another `slot`"
))
})?;
Ok(CardDevice { card, slot })
}
#[must_use]
pub fn card(&self) -> &Arc<SdCard> {
&self.card
}
#[must_use]
pub fn slot(&self) -> &str {
&self.slot
}
}
impl Device for CardDevice {
fn class(&self) -> &'static DeviceClass {
&CLASS
}
fn realize(&self, _ctx: &mut RealizeCtx<'_>) -> Result<()> {
Ok(())
}
fn reset(&self, _kind: ResetKind) {
self.card.power_cycle();
}
fn save(&self, w: &mut ChunkWriter<'_>) -> Result<()> {
self.card.save(w)
}
fn load(&self, r: &mut ChunkReader<'_>) -> Result<()> {
self.card.load(r)
}
}
impl Instance for CardDevice {}
pub fn register(registry: &mut crate::core::Registry) -> Result<()> {
registry.add(&CLASS)
}
pub fn bind(bindings: &mut crate::machine::Bindings) -> Result<()> {
bindings.bind(CLASS_NAME, |props| Ok(Arc::new(CardDevice::new(props)?)))
}
#[must_use]
pub fn schema() -> ClassSchema {
ClassSchema::new(CLASS_NAME)
.prop(PropSchema::new("size", ValueKind::Size).required())
.prop(PropSchema::new("image", ValueKind::Media))
.prop(PropSchema::new("slot", ValueKind::Str))
.prop(PropSchema::new("high-capacity", ValueKind::Bool))
.prop(PropSchema::new("readonly", ValueKind::Bool))
.prop(PropSchema::new("mode", ValueKind::Str).values(&["sd", "spi"]))
.prop(PropSchema::new("manufacturer", ValueKind::Uint).range(0, 0xff))
.prop(PropSchema::new("oem", ValueKind::Str))
.prop(PropSchema::new("product", ValueKind::Str))
.prop(PropSchema::new("revision", ValueKind::Uint).range(0, 0xff))
.prop(PropSchema::new("serial", ValueKind::Uint).range(0, 0xffff_ffff))
.prop(PropSchema::new("year", ValueKind::Uint).range(2000, 2255))
.prop(PropSchema::new("month", ValueKind::Uint).range(1, 12))
.prop(PropSchema::new("rca", ValueKind::Uint).range(1, 0xffff))
}
#[cfg(test)]
mod tests;