use super::sdmmc_proto::*;
use super::{Block, BlockCount, BlockDevice, BlockIdx};
use core::cell::UnsafeCell;
use nb::block;
const DEFAULT_DELAY_COUNT: u32 = 32_000;
pub struct SdMmcSpi<SPI, CS>
where
SPI: embedded_hal::spi::FullDuplex<u8>,
CS: embedded_hal::digital::OutputPin,
<SPI as embedded_hal::spi::FullDuplex<u8>>::Error: core::fmt::Debug,
{
spi: UnsafeCell<SPI>,
cs: UnsafeCell<CS>,
card_type: CardType,
state: State,
}
#[derive(Debug, Copy, Clone)]
pub enum Error {
Transport,
CantEnableCRC,
TimeoutReadBuffer,
TimeoutWaitNotBusy,
TimeoutCommand(u8),
TimeoutACommand(u8),
Cmd58Error,
RegisterReadError,
CrcError(u16, u16),
ReadError,
WriteError,
BadState,
CardNotFound,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum State {
NoInit,
Error,
Idle,
}
#[derive(Debug, Copy, Clone, PartialEq)]
enum CardType {
SD1,
SD2,
SDHC,
}
struct Delay(u32);
impl Delay {
fn new() -> Delay {
Delay(DEFAULT_DELAY_COUNT)
}
fn delay(&mut self, err: Error) -> Result<(), Error> {
if self.0 == 0 {
Err(err)
} else {
let dummy_var: u32 = 0;
for _ in 0..100 {
unsafe { core::ptr::read_volatile(&dummy_var) };
}
self.0 -= 1;
Ok(())
}
}
}
impl<SPI, CS> SdMmcSpi<SPI, CS>
where
SPI: embedded_hal::spi::FullDuplex<u8>,
CS: embedded_hal::digital::OutputPin,
<SPI as embedded_hal::spi::FullDuplex<u8>>::Error: core::fmt::Debug,
{
pub fn new(spi: SPI, cs: CS) -> SdMmcSpi<SPI, CS> {
SdMmcSpi {
spi: UnsafeCell::new(spi),
cs: UnsafeCell::new(cs),
card_type: CardType::SD1,
state: State::NoInit,
}
}
pub fn spi(&mut self) -> &mut SPI {
unsafe { &mut *self.spi.get() }
}
fn cs_high(&self) {
let cs = unsafe { &mut *self.cs.get() };
cs.set_high();
}
fn cs_low(&self) {
let cs = unsafe { &mut *self.cs.get() };
cs.set_low();
}
pub fn init(&mut self) -> Result<(), Error> {
let f = |s: &mut Self| {
s.state = State::Error;
s.cs_high();
for _ in 0..10 {
s.send(0xFF)?;
}
s.cs_low();
let mut attempts = 32;
while attempts > 0 {
match s.card_command(CMD0, 0) {
Err(Error::TimeoutCommand(0)) => {
attempts -= 1;
}
Err(e) => {
return Err(e);
}
Ok(R1_IDLE_STATE) => {
break;
}
Ok(_) => {
}
}
}
if attempts == 0 {
return Err(Error::CardNotFound);
}
if s.card_command(CMD59, 1)? != R1_IDLE_STATE {
return Err(Error::CantEnableCRC);
}
let mut delay = Delay::new();
loop {
if s.card_command(CMD8, 0x1AA)? == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE) {
s.card_type = CardType::SD1;
break;
}
s.receive()?;
s.receive()?;
s.receive()?;
let status = s.receive()?;
if status == 0xAA {
s.card_type = CardType::SD2;
break;
}
delay.delay(Error::TimeoutCommand(CMD8))?;
}
let arg = match s.card_type {
CardType::SD1 => 0,
CardType::SD2 | CardType::SDHC => 0x4000_0000,
};
let mut delay = Delay::new();
while s.card_acmd(ACMD41, arg)? != R1_READY_STATE {
delay.delay(Error::TimeoutACommand(ACMD41))?;
}
if s.card_type == CardType::SD2 {
if s.card_command(CMD58, 0)? != 0 {
return Err(Error::Cmd58Error);
}
if (s.receive()? & 0xC0) == 0xC0 {
s.card_type = CardType::SDHC;
}
s.receive()?;
s.receive()?;
s.receive()?;
}
s.state = State::Idle;
Ok(())
};
let result = f(self);
self.cs_high();
let _ = self.receive();
result
}
pub fn deinit(&mut self) {
self.state = State::NoInit;
}
pub fn card_size_bytes(&self) -> Result<u64, Error> {
self.check_state()?;
self.with_chip_select(|s| {
let csd = s.read_csd()?;
match csd {
Csd::V1(ref contents) => Ok(contents.card_capacity_bytes()),
Csd::V2(ref contents) => Ok(contents.card_capacity_bytes()),
}
})
}
pub fn erase(&mut self, _first_block: BlockIdx, _last_block: BlockIdx) -> Result<(), Error> {
self.check_state()?;
unimplemented!();
}
pub fn erase_single_block_enabled(&self) -> Result<bool, Error> {
self.check_state()?;
self.with_chip_select(|s| {
let csd = s.read_csd()?;
match csd {
Csd::V1(ref contents) => Ok(contents.erase_single_block_enabled()),
Csd::V2(ref contents) => Ok(contents.erase_single_block_enabled()),
}
})
}
fn check_state(&self) -> Result<(), Error> {
if self.state != State::Idle {
Err(Error::BadState)
} else {
Ok(())
}
}
fn with_chip_select_mut<F, T>(&self, func: F) -> T
where
F: FnOnce(&Self) -> T,
{
self.cs_low();
let result = func(self);
self.cs_high();
result
}
fn with_chip_select<F, T>(&self, func: F) -> T
where
F: FnOnce(&Self) -> T,
{
self.cs_low();
let result = func(self);
self.cs_high();
result
}
fn read_csd(&self) -> Result<Csd, Error> {
match self.card_type {
CardType::SD1 => {
let mut csd = CsdV1::new();
if self.card_command(CMD9, 0)? != 0 {
return Err(Error::RegisterReadError);
}
self.read_data(&mut csd.data)?;
Ok(Csd::V1(csd))
}
CardType::SD2 | CardType::SDHC => {
let mut csd = CsdV2::new();
if self.card_command(CMD9, 0)? != 0 {
return Err(Error::RegisterReadError);
}
self.read_data(&mut csd.data)?;
Ok(Csd::V2(csd))
}
}
}
fn read_data(&self, buffer: &mut [u8]) -> Result<(), Error> {
let mut delay = Delay::new();
let status = loop {
let s = self.receive()?;
if s != 0xFF {
break s;
}
delay.delay(Error::TimeoutReadBuffer)?;
};
if status != DATA_START_BLOCK {
return Err(Error::ReadError);
}
for b in buffer.iter_mut() {
*b = self.receive()?;
}
let mut crc = u16::from(self.receive()?);
crc <<= 8;
crc |= u16::from(self.receive()?);
let calc_crc = crc16(buffer);
if crc != calc_crc {
return Err(Error::CrcError(crc, calc_crc));
}
Ok(())
}
fn write_data(&self, token: u8, buffer: &[u8]) -> Result<(), Error> {
let calc_crc = crc16(buffer);
self.send(token)?;
for &b in buffer.iter() {
self.send(b)?;
}
self.send((calc_crc >> 16) as u8)?;
self.send(calc_crc as u8)?;
let status = self.receive()?;
if (status & DATA_RES_MASK) != DATA_RES_ACCEPTED {
Err(Error::WriteError)
} else {
Ok(())
}
}
fn card_acmd(&self, command: u8, arg: u32) -> Result<u8, Error> {
self.card_command(CMD55, 0)?;
self.card_command(command, arg)
}
fn card_command(&self, command: u8, arg: u32) -> Result<u8, Error> {
self.wait_not_busy()?;
let mut buf = [
0x40 | command,
(arg >> 24) as u8,
(arg >> 16) as u8,
(arg >> 8) as u8,
arg as u8,
0,
];
buf[5] = crc7(&buf[0..5]);
for b in buf.iter() {
self.send(*b)?;
}
if command == CMD12 {
let _result = self.receive()?;
}
for _ in 0..512 {
let result = self.receive()?;
if (result & 0x80) == 0 {
return Ok(result);
}
}
Err(Error::TimeoutCommand(command))
}
fn receive(&self) -> Result<u8, Error> {
self.transfer(0xFF)
}
fn send(&self, out: u8) -> Result<(), Error> {
let _ = self.transfer(out)?;
Ok(())
}
fn transfer(&self, out: u8) -> Result<u8, Error> {
let spi = unsafe { &mut *self.spi.get() };
block!(spi.send(out)).map_err(|_e| Error::Transport)?;
block!(spi.read()).map_err(|_e| Error::Transport)
}
fn wait_not_busy(&self) -> Result<(), Error> {
let mut delay = Delay::new();
loop {
let s = self.receive()?;
if s == 0xFF {
break;
}
delay.delay(Error::TimeoutWaitNotBusy)?;
}
Ok(())
}
}
impl<SPI, CS> BlockDevice for SdMmcSpi<SPI, CS>
where
SPI: embedded_hal::spi::FullDuplex<u8>,
<SPI as embedded_hal::spi::FullDuplex<u8>>::Error: core::fmt::Debug,
CS: embedded_hal::digital::OutputPin,
{
type Error = Error;
fn read(
&self,
blocks: &mut [Block],
start_block_idx: BlockIdx,
_reason: &str,
) -> Result<(), Self::Error> {
self.check_state()?;
let start_idx = match self.card_type {
CardType::SD1 | CardType::SD2 => start_block_idx.0 * 512,
CardType::SDHC => start_block_idx.0,
};
self.with_chip_select(|s| {
if blocks.len() == 1 {
s.card_command(CMD17, start_idx)?;
s.read_data(&mut blocks[0].contents)?;
} else {
s.card_command(CMD18, start_idx)?;
for block in blocks.iter_mut() {
s.read_data(&mut block.contents)?;
}
s.card_command(CMD12, 0)?;
}
Ok(())
})
}
fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
self.check_state()?;
let start_idx = match self.card_type {
CardType::SD1 | CardType::SD2 => start_block_idx.0 * 512,
CardType::SDHC => start_block_idx.0,
};
self.with_chip_select_mut(|s| {
if blocks.len() == 1 {
s.card_command(CMD24, start_idx)?;
s.write_data(DATA_START_BLOCK, &blocks[0].contents)?;
s.wait_not_busy()?;
if s.card_command(CMD13, 0)? != 0x00 {
return Err(Error::WriteError);
}
if s.receive()? != 0x00 {
return Err(Error::WriteError);
}
} else {
s.card_command(CMD25, start_idx)?;
for block in blocks.iter() {
s.wait_not_busy()?;
s.write_data(WRITE_MULTIPLE_TOKEN, &block.contents)?;
}
s.wait_not_busy()?;
s.send(STOP_TRAN_TOKEN)?;
}
Ok(())
})
}
fn num_blocks(&self) -> Result<BlockCount, Self::Error> {
let num_bytes = self.card_size_bytes()?;
let num_blocks = (num_bytes / 512) as u32;
Ok(BlockCount(num_blocks))
}
}