From b89008001a0f413b88f32b09c3f72f57aeecdb8a Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Sun, 22 Dec 2024 19:00:12 +0800 Subject: [PATCH 1/5] sdcard: refactor, move SPI-based SD card inner into a separate module Signed-off-by: Zhouqi Jiang --- src/sdcard/mod.rs | 399 ++------------------------------------------ src/sdcard/spi.rs | 415 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 425 insertions(+), 389 deletions(-) create mode 100644 src/sdcard/spi.rs diff --git a/src/sdcard/mod.rs b/src/sdcard/mod.rs index 553791f..4623fac 100644 --- a/src/sdcard/mod.rs +++ b/src/sdcard/mod.rs @@ -1,19 +1,19 @@ -//! Implements the BlockDevice trait for an SD/MMC Protocol over SPI. +//! Implements the BlockDevice trait for an SD/MMC Protocol. //! //! This is currently optimised for readability and debugability, not //! performance. pub mod proto; +mod spi; -use crate::{trace, Block, BlockCount, BlockDevice, BlockIdx}; +use crate::{Block, BlockCount, BlockDevice, BlockIdx}; use core::cell::RefCell; -use proto::*; // **************************************************************************** // Imports // **************************************************************************** -use crate::{debug, warn}; +use crate::debug; // **************************************************************************** // Types and Implementations @@ -40,7 +40,7 @@ where SPI: embedded_hal::spi::SpiDevice, DELAYER: embedded_hal::delay::DelayNs, { - inner: RefCell>, + inner: RefCell>, } impl SdCard @@ -71,12 +71,7 @@ where options: AcquireOpts, ) -> SdCard { SdCard { - inner: RefCell::new(SdCardInner { - spi, - delayer, - card_type: None, - options, - }), + inner: RefCell::new(spi::SpiSdCardInner::new(spi, delayer, options)), } } @@ -92,7 +87,7 @@ where F: FnOnce(&mut SPI) -> T, { let mut inner = self.inner.borrow_mut(); - func(&mut inner.spi) + inner.spi(func) } /// Return the usable size of this SD card in bytes. @@ -118,7 +113,7 @@ where /// The next operation will assume the card has been freshly inserted. pub fn mark_card_uninit(&self) { let mut inner = self.inner.borrow_mut(); - inner.card_type = None; + inner.mark_card_uninit(); } /// Get the card type. @@ -127,7 +122,7 @@ where pub fn get_card_type(&self) -> Option { let mut inner = self.inner.borrow_mut(); inner.check_init().ok()?; - inner.card_type + inner.get_card_type() } /// Tell the driver the card has been initialised. @@ -148,7 +143,7 @@ where /// data corruption. pub unsafe fn mark_card_as_init(&self, card_type: CardType) { let mut inner = self.inner.borrow_mut(); - inner.card_type = Some(card_type); + inner.mark_card_as_init(card_type); } } @@ -189,380 +184,6 @@ where } } -/// Inner details for the SD Card driver. -/// -/// All the APIs required `&mut self`. -struct SdCardInner -where - SPI: embedded_hal::spi::SpiDevice, - DELAYER: embedded_hal::delay::DelayNs, -{ - spi: SPI, - delayer: DELAYER, - card_type: Option, - options: AcquireOpts, -} - -impl SdCardInner -where - SPI: embedded_hal::spi::SpiDevice, - DELAYER: embedded_hal::delay::DelayNs, -{ - /// Read one or more blocks, starting at the given block index. - fn read(&mut self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Error> { - let start_idx = match self.card_type { - Some(CardType::SD1 | CardType::SD2) => start_block_idx.0 * 512, - Some(CardType::SDHC) => start_block_idx.0, - None => return Err(Error::CardNotFound), - }; - - if blocks.len() == 1 { - // Start a single-block read - self.card_command(CMD17, start_idx)?; - self.read_data(&mut blocks[0].contents)?; - } else { - // Start a multi-block read - self.card_command(CMD18, start_idx)?; - for block in blocks.iter_mut() { - self.read_data(&mut block.contents)?; - } - // Stop the read - self.card_command(CMD12, 0)?; - } - Ok(()) - } - - /// Write one or more blocks, starting at the given block index. - fn write(&mut self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Error> { - let start_idx = match self.card_type { - Some(CardType::SD1 | CardType::SD2) => start_block_idx.0 * 512, - Some(CardType::SDHC) => start_block_idx.0, - None => return Err(Error::CardNotFound), - }; - if blocks.len() == 1 { - // Start a single-block write - self.card_command(CMD24, start_idx)?; - self.write_data(DATA_START_BLOCK, &blocks[0].contents)?; - self.wait_not_busy(Delay::new_write())?; - if self.card_command(CMD13, 0)? != 0x00 { - return Err(Error::WriteError); - } - if self.read_byte()? != 0x00 { - return Err(Error::WriteError); - } - } else { - // > It is recommended using this command preceding CMD25, some of the cards will be faster for Multiple - // > Write Blocks operation. Note that the host should send ACMD23 just before WRITE command if the host - // > wants to use the pre-erased feature - self.card_acmd(ACMD23, blocks.len() as u32)?; - // wait for card to be ready before sending the next command - self.wait_not_busy(Delay::new_write())?; - - // Start a multi-block write - self.card_command(CMD25, start_idx)?; - for block in blocks.iter() { - self.wait_not_busy(Delay::new_write())?; - self.write_data(WRITE_MULTIPLE_TOKEN, &block.contents)?; - } - // Stop the write - self.wait_not_busy(Delay::new_write())?; - self.write_byte(STOP_TRAN_TOKEN)?; - } - Ok(()) - } - - /// Determine how many blocks this device can hold. - fn num_blocks(&mut self) -> Result { - let csd = self.read_csd()?; - debug!("CSD: {:?}", csd); - let num_blocks = match csd { - Csd::V1(ref contents) => contents.card_capacity_blocks(), - Csd::V2(ref contents) => contents.card_capacity_blocks(), - }; - Ok(BlockCount(num_blocks)) - } - - /// Return the usable size of this SD card in bytes. - fn num_bytes(&mut self) -> Result { - let csd = self.read_csd()?; - debug!("CSD: {:?}", csd); - match csd { - Csd::V1(ref contents) => Ok(contents.card_capacity_bytes()), - Csd::V2(ref contents) => Ok(contents.card_capacity_bytes()), - } - } - - /// Can this card erase single blocks? - pub fn erase_single_block_enabled(&mut self) -> Result { - let csd = self.read_csd()?; - match csd { - Csd::V1(ref contents) => Ok(contents.erase_single_block_enabled()), - Csd::V2(ref contents) => Ok(contents.erase_single_block_enabled()), - } - } - - /// Read the 'card specific data' block. - fn read_csd(&mut self) -> Result { - match self.card_type { - Some(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)) - } - Some(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)) - } - None => Err(Error::CardNotFound), - } - } - - /// Read an arbitrary number of bytes from the card using the SD Card - /// protocol and an optional CRC. Always fills the given buffer, so make - /// sure it's the right size. - fn read_data(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - // Get first non-FF byte. - let mut delay = Delay::new_read(); - let status = loop { - let s = self.read_byte()?; - if s != 0xFF { - break s; - } - delay.delay(&mut self.delayer, Error::TimeoutReadBuffer)?; - }; - if status != DATA_START_BLOCK { - return Err(Error::ReadError); - } - - buffer.fill(0xFF); - self.transfer_bytes(buffer)?; - - // These two bytes are always sent. They are either a valid CRC, or - // junk, depending on whether CRC mode was enabled. - let mut crc_bytes = [0xFF; 2]; - self.transfer_bytes(&mut crc_bytes)?; - if self.options.use_crc { - let crc = u16::from_be_bytes(crc_bytes); - let calc_crc = crc16(buffer); - if crc != calc_crc { - return Err(Error::CrcError(crc, calc_crc)); - } - } - - Ok(()) - } - - /// Write an arbitrary number of bytes to the card using the SD protocol and - /// an optional CRC. - fn write_data(&mut self, token: u8, buffer: &[u8]) -> Result<(), Error> { - self.write_byte(token)?; - self.write_bytes(buffer)?; - let crc_bytes = if self.options.use_crc { - crc16(buffer).to_be_bytes() - } else { - [0xFF, 0xFF] - }; - // These two bytes are always sent. They are either a valid CRC, or - // junk, depending on whether CRC mode was enabled. - self.write_bytes(&crc_bytes)?; - - let status = self.read_byte()?; - if (status & DATA_RES_MASK) != DATA_RES_ACCEPTED { - Err(Error::WriteError) - } else { - Ok(()) - } - } - - /// Check the card is initialised. - fn check_init(&mut self) -> Result<(), Error> { - if self.card_type.is_none() { - // If we don't know what the card type is, try and initialise the - // card. This will tell us what type of card it is. - self.acquire() - } else { - Ok(()) - } - } - - /// Initializes the card into a known state (or at least tries to). - fn acquire(&mut self) -> Result<(), Error> { - debug!("acquiring card with opts: {:?}", self.options); - let f = |s: &mut Self| { - // Assume it hasn't worked - let mut card_type; - trace!("Reset card.."); - // Enter SPI mode. - let mut delay = Delay::new(s.options.acquire_retries); - for _attempts in 1.. { - trace!("Enter SPI mode, attempt: {}..", _attempts); - match s.card_command(CMD0, 0) { - Err(Error::TimeoutCommand(0)) => { - // Try again? - warn!("Timed out, trying again.."); - // Try flushing the card as done here: https://github.com/greiman/SdFat/blob/master/src/SdCard/SdSpiCard.cpp#L170, - // https://github.com/rust-embedded-community/embedded-sdmmc-rs/pull/65#issuecomment-1270709448 - for _ in 0..0xFF { - s.write_byte(0xFF)?; - } - } - Err(e) => { - return Err(e); - } - Ok(R1_IDLE_STATE) => { - break; - } - Ok(_r) => { - // Try again - warn!("Got response: {:x}, trying again..", _r); - } - } - - delay.delay(&mut s.delayer, Error::CardNotFound)?; - } - // Enable CRC - debug!("Enable CRC: {}", s.options.use_crc); - // "The SPI interface is initialized in the CRC OFF mode in default" - // -- SD Part 1 Physical Layer Specification v9.00, Section 7.2.2 Bus Transfer Protection - if s.options.use_crc && s.card_command(CMD59, 1)? != R1_IDLE_STATE { - return Err(Error::CantEnableCRC); - } - // Check card version - let mut delay = Delay::new_command(); - let arg = loop { - if s.card_command(CMD8, 0x1AA)? == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE) { - card_type = CardType::SD1; - break 0; - } - let mut buffer = [0xFF; 4]; - s.transfer_bytes(&mut buffer)?; - let status = buffer[3]; - if status == 0xAA { - card_type = CardType::SD2; - break 0x4000_0000; - } - delay.delay(&mut s.delayer, Error::TimeoutCommand(CMD8))?; - }; - - let mut delay = Delay::new_command(); - while s.card_acmd(ACMD41, arg)? != R1_READY_STATE { - delay.delay(&mut s.delayer, Error::TimeoutACommand(ACMD41))?; - } - - if card_type == CardType::SD2 { - if s.card_command(CMD58, 0)? != 0 { - return Err(Error::Cmd58Error); - } - let mut buffer = [0xFF; 4]; - s.transfer_bytes(&mut buffer)?; - if (buffer[0] & 0xC0) == 0xC0 { - card_type = CardType::SDHC; - } - // Ignore the other three bytes - } - debug!("Card version: {:?}", card_type); - s.card_type = Some(card_type); - Ok(()) - }; - let result = f(self); - let _ = self.read_byte(); - result - } - - /// Perform an application-specific command. - fn card_acmd(&mut self, command: u8, arg: u32) -> Result { - self.card_command(CMD55, 0)?; - self.card_command(command, arg) - } - - /// Perform a command. - fn card_command(&mut self, command: u8, arg: u32) -> Result { - if command != CMD0 && command != CMD12 { - self.wait_not_busy(Delay::new_command())?; - } - - 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]); - - self.write_bytes(&buf)?; - - // skip stuff byte for stop read - if command == CMD12 { - let _result = self.read_byte()?; - } - - let mut delay = Delay::new_command(); - loop { - let result = self.read_byte()?; - if (result & 0x80) == ERROR_OK { - return Ok(result); - } - delay.delay(&mut self.delayer, Error::TimeoutCommand(command))?; - } - } - - /// Receive a byte from the SPI bus by clocking out an 0xFF byte. - fn read_byte(&mut self) -> Result { - self.transfer_byte(0xFF) - } - - /// Send a byte over the SPI bus and ignore what comes back. - fn write_byte(&mut self, out: u8) -> Result<(), Error> { - let _ = self.transfer_byte(out)?; - Ok(()) - } - - /// Send one byte and receive one byte over the SPI bus. - fn transfer_byte(&mut self, out: u8) -> Result { - let mut read_buf = [0u8; 1]; - self.spi - .transfer(&mut read_buf, &[out]) - .map_err(|_| Error::Transport)?; - Ok(read_buf[0]) - } - - /// Send multiple bytes and ignore what comes back over the SPI bus. - fn write_bytes(&mut self, out: &[u8]) -> Result<(), Error> { - self.spi.write(out).map_err(|_e| Error::Transport)?; - Ok(()) - } - - /// Send multiple bytes and replace them with what comes back over the SPI bus. - fn transfer_bytes(&mut self, in_out: &mut [u8]) -> Result<(), Error> { - self.spi - .transfer_in_place(in_out) - .map_err(|_e| Error::Transport)?; - Ok(()) - } - - /// Spin until the card returns 0xFF, or we spin too many times and - /// timeout. - fn wait_not_busy(&mut self, mut delay: Delay) -> Result<(), Error> { - loop { - let s = self.read_byte()?; - if s == 0xFF { - break; - } - delay.delay(&mut self.delayer, Error::TimeoutWaitNotBusy)?; - } - Ok(()) - } -} - /// Options for acquiring the card. #[cfg_attr(feature = "defmt-log", derive(defmt::Format))] #[derive(Debug)] diff --git a/src/sdcard/spi.rs b/src/sdcard/spi.rs new file mode 100644 index 0000000..8bb2391 --- /dev/null +++ b/src/sdcard/spi.rs @@ -0,0 +1,415 @@ +//! Implements the BlockDevice trait for an SD/MMC Protocol over SPI. +//! +//! This is currently optimised for readability and debugability, not +//! performance. + +use super::{proto::*, AcquireOpts, CardType, Delay, Error}; +use crate::blockdevice::{Block, BlockCount, BlockIdx}; +use crate::{debug, trace, warn}; + +/// Inner details for the SD Card driver. +/// +/// All the APIs required `&mut self`. +pub struct SpiSdCardInner +where + SPI: embedded_hal::spi::SpiDevice, + DELAYER: embedded_hal::delay::DelayNs, +{ + spi: SPI, + delayer: DELAYER, + card_type: Option, + options: AcquireOpts, +} + +impl SpiSdCardInner +where + SPI: embedded_hal::spi::SpiDevice, + DELAYER: embedded_hal::delay::DelayNs, +{ + /// Construct a new raw SPI transport interface for SD/MMC Card. + pub fn new(spi: SPI, delayer: DELAYER, options: AcquireOpts) -> Self { + SpiSdCardInner { + spi, + delayer, + card_type: None, + options, + } + } + /// Get a temporary borrow on the underlying SPI device. + pub fn spi(&mut self, func: F) -> T + where + F: FnOnce(&mut SPI) -> T, + { + func(&mut self.spi) + } + /// Read one or more blocks, starting at the given block index. + pub fn read(&mut self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Error> { + let start_idx = match self.card_type { + Some(CardType::SD1 | CardType::SD2) => start_block_idx.0 * 512, + Some(CardType::SDHC) => start_block_idx.0, + None => return Err(Error::CardNotFound), + }; + + if blocks.len() == 1 { + // Start a single-block read + self.card_command(CMD17, start_idx)?; + self.read_data(&mut blocks[0].contents)?; + } else { + // Start a multi-block read + self.card_command(CMD18, start_idx)?; + for block in blocks.iter_mut() { + self.read_data(&mut block.contents)?; + } + // Stop the read + self.card_command(CMD12, 0)?; + } + Ok(()) + } + + /// Write one or more blocks, starting at the given block index. + pub fn write(&mut self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Error> { + let start_idx = match self.card_type { + Some(CardType::SD1 | CardType::SD2) => start_block_idx.0 * 512, + Some(CardType::SDHC) => start_block_idx.0, + None => return Err(Error::CardNotFound), + }; + if blocks.len() == 1 { + // Start a single-block write + self.card_command(CMD24, start_idx)?; + self.write_data(DATA_START_BLOCK, &blocks[0].contents)?; + self.wait_not_busy(Delay::new_write())?; + if self.card_command(CMD13, 0)? != 0x00 { + return Err(Error::WriteError); + } + if self.read_byte()? != 0x00 { + return Err(Error::WriteError); + } + } else { + // > It is recommended using this command preceding CMD25, some of the cards will be faster for Multiple + // > Write Blocks operation. Note that the host should send ACMD23 just before WRITE command if the host + // > wants to use the pre-erased feature + self.card_acmd(ACMD23, blocks.len() as u32)?; + // wait for card to be ready before sending the next command + self.wait_not_busy(Delay::new_write())?; + + // Start a multi-block write + self.card_command(CMD25, start_idx)?; + for block in blocks.iter() { + self.wait_not_busy(Delay::new_write())?; + self.write_data(WRITE_MULTIPLE_TOKEN, &block.contents)?; + } + // Stop the write + self.wait_not_busy(Delay::new_write())?; + self.write_byte(STOP_TRAN_TOKEN)?; + } + Ok(()) + } + + /// Determine how many blocks this device can hold. + pub fn num_blocks(&mut self) -> Result { + let csd = self.read_csd()?; + debug!("CSD: {:?}", csd); + let num_blocks = match csd { + Csd::V1(ref contents) => contents.card_capacity_blocks(), + Csd::V2(ref contents) => contents.card_capacity_blocks(), + }; + Ok(BlockCount(num_blocks)) + } + + /// Return the usable size of this SD card in bytes. + pub fn num_bytes(&mut self) -> Result { + let csd = self.read_csd()?; + debug!("CSD: {:?}", csd); + match csd { + Csd::V1(ref contents) => Ok(contents.card_capacity_bytes()), + Csd::V2(ref contents) => Ok(contents.card_capacity_bytes()), + } + } + + /// Can this card erase single blocks? + pub fn erase_single_block_enabled(&mut self) -> Result { + let csd = self.read_csd()?; + match csd { + Csd::V1(ref contents) => Ok(contents.erase_single_block_enabled()), + Csd::V2(ref contents) => Ok(contents.erase_single_block_enabled()), + } + } + + /// Read the 'card specific data' block. + fn read_csd(&mut self) -> Result { + match self.card_type { + Some(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)) + } + Some(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)) + } + None => Err(Error::CardNotFound), + } + } + + /// Read an arbitrary number of bytes from the card using the SD Card + /// protocol and an optional CRC. Always fills the given buffer, so make + /// sure it's the right size. + fn read_data(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + // Get first non-FF byte. + let mut delay = Delay::new_read(); + let status = loop { + let s = self.read_byte()?; + if s != 0xFF { + break s; + } + delay.delay(&mut self.delayer, Error::TimeoutReadBuffer)?; + }; + if status != DATA_START_BLOCK { + return Err(Error::ReadError); + } + + buffer.fill(0xFF); + self.transfer_bytes(buffer)?; + + // These two bytes are always sent. They are either a valid CRC, or + // junk, depending on whether CRC mode was enabled. + let mut crc_bytes = [0xFF; 2]; + self.transfer_bytes(&mut crc_bytes)?; + if self.options.use_crc { + let crc = u16::from_be_bytes(crc_bytes); + let calc_crc = crc16(buffer); + if crc != calc_crc { + return Err(Error::CrcError(crc, calc_crc)); + } + } + + Ok(()) + } + + /// Write an arbitrary number of bytes to the card using the SD protocol and + /// an optional CRC. + fn write_data(&mut self, token: u8, buffer: &[u8]) -> Result<(), Error> { + self.write_byte(token)?; + self.write_bytes(buffer)?; + let crc_bytes = if self.options.use_crc { + crc16(buffer).to_be_bytes() + } else { + [0xFF, 0xFF] + }; + // These two bytes are always sent. They are either a valid CRC, or + // junk, depending on whether CRC mode was enabled. + self.write_bytes(&crc_bytes)?; + + let status = self.read_byte()?; + if (status & DATA_RES_MASK) != DATA_RES_ACCEPTED { + Err(Error::WriteError) + } else { + Ok(()) + } + } + + /// Check the card is initialised. + pub fn check_init(&mut self) -> Result<(), Error> { + if self.card_type.is_none() { + // If we don't know what the card type is, try and initialise the + // card. This will tell us what type of card it is. + self.acquire() + } else { + Ok(()) + } + } + + /// Initializes the card into a known state (or at least tries to). + pub fn acquire(&mut self) -> Result<(), Error> { + debug!("acquiring card with opts: {:?}", self.options); + let f = |s: &mut Self| { + // Assume it hasn't worked + let mut card_type; + trace!("Reset card.."); + // Enter SPI mode. + let mut delay = Delay::new(s.options.acquire_retries); + for _attempts in 1.. { + trace!("Enter SPI mode, attempt: {}..", _attempts); + match s.card_command(CMD0, 0) { + Err(Error::TimeoutCommand(0)) => { + // Try again? + warn!("Timed out, trying again.."); + // Try flushing the card as done here: https://github.com/greiman/SdFat/blob/master/src/SdCard/SdSpiCard.cpp#L170, + // https://github.com/rust-embedded-community/embedded-sdmmc-rs/pull/65#issuecomment-1270709448 + for _ in 0..0xFF { + s.write_byte(0xFF)?; + } + } + Err(e) => { + return Err(e); + } + Ok(R1_IDLE_STATE) => { + break; + } + Ok(_r) => { + // Try again + warn!("Got response: {:x}, trying again..", _r); + } + } + + delay.delay(&mut s.delayer, Error::CardNotFound)?; + } + // Enable CRC + debug!("Enable CRC: {}", s.options.use_crc); + // "The SPI interface is initialized in the CRC OFF mode in default" + // -- SD Part 1 Physical Layer Specification v9.00, Section 7.2.2 Bus Transfer Protection + if s.options.use_crc && s.card_command(CMD59, 1)? != R1_IDLE_STATE { + return Err(Error::CantEnableCRC); + } + // Check card version + let mut delay = Delay::new_command(); + let arg = loop { + if s.card_command(CMD8, 0x1AA)? == (R1_ILLEGAL_COMMAND | R1_IDLE_STATE) { + card_type = CardType::SD1; + break 0; + } + let mut buffer = [0xFF; 4]; + s.transfer_bytes(&mut buffer)?; + let status = buffer[3]; + if status == 0xAA { + card_type = CardType::SD2; + break 0x4000_0000; + } + delay.delay(&mut s.delayer, Error::TimeoutCommand(CMD8))?; + }; + + let mut delay = Delay::new_command(); + while s.card_acmd(ACMD41, arg)? != R1_READY_STATE { + delay.delay(&mut s.delayer, Error::TimeoutACommand(ACMD41))?; + } + + if card_type == CardType::SD2 { + if s.card_command(CMD58, 0)? != 0 { + return Err(Error::Cmd58Error); + } + let mut buffer = [0xFF; 4]; + s.transfer_bytes(&mut buffer)?; + if (buffer[0] & 0xC0) == 0xC0 { + card_type = CardType::SDHC; + } + // Ignore the other three bytes + } + debug!("Card version: {:?}", card_type); + s.card_type = Some(card_type); + Ok(()) + }; + let result = f(self); + let _ = self.read_byte(); + result + } + + /// Mark the card as requiring a reset. + /// + /// The next operation will assume the card has been freshly inserted. + pub fn mark_card_uninit(&mut self) { + self.card_type = None; + } + + /// Get the card type. + pub fn get_card_type(&self) -> Option { + self.card_type + } + + /// Tell the driver the card has been initialised. + pub unsafe fn mark_card_as_init(&mut self, card_type: CardType) { + self.card_type = Some(card_type); + } + + /// Perform an application-specific command. + fn card_acmd(&mut self, command: u8, arg: u32) -> Result { + self.card_command(CMD55, 0)?; + self.card_command(command, arg) + } + + /// Perform a command. + fn card_command(&mut self, command: u8, arg: u32) -> Result { + if command != CMD0 && command != CMD12 { + self.wait_not_busy(Delay::new_command())?; + } + + 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]); + + self.write_bytes(&buf)?; + + // skip stuff byte for stop read + if command == CMD12 { + let _result = self.read_byte()?; + } + + let mut delay = Delay::new_command(); + loop { + let result = self.read_byte()?; + if (result & 0x80) == ERROR_OK { + return Ok(result); + } + delay.delay(&mut self.delayer, Error::TimeoutCommand(command))?; + } + } + + /// Receive a byte from the SPI bus by clocking out an 0xFF byte. + fn read_byte(&mut self) -> Result { + self.transfer_byte(0xFF) + } + + /// Send a byte over the SPI bus and ignore what comes back. + fn write_byte(&mut self, out: u8) -> Result<(), Error> { + let _ = self.transfer_byte(out)?; + Ok(()) + } + + /// Send one byte and receive one byte over the SPI bus. + fn transfer_byte(&mut self, out: u8) -> Result { + let mut read_buf = [0u8; 1]; + self.spi + .transfer(&mut read_buf, &[out]) + .map_err(|_| Error::Transport)?; + Ok(read_buf[0]) + } + + /// Send multiple bytes and ignore what comes back over the SPI bus. + fn write_bytes(&mut self, out: &[u8]) -> Result<(), Error> { + self.spi.write(out).map_err(|_e| Error::Transport)?; + Ok(()) + } + + /// Send multiple bytes and replace them with what comes back over the SPI bus. + fn transfer_bytes(&mut self, in_out: &mut [u8]) -> Result<(), Error> { + self.spi + .transfer_in_place(in_out) + .map_err(|_e| Error::Transport)?; + Ok(()) + } + + /// Spin until the card returns 0xFF, or we spin too many times and + /// timeout. + fn wait_not_busy(&mut self, mut delay: Delay) -> Result<(), Error> { + loop { + let s = self.read_byte()?; + if s == 0xFF { + break; + } + delay.delay(&mut self.delayer, Error::TimeoutWaitNotBusy)?; + } + Ok(()) + } +} From ab17293d497fcc577072325bde1b8081b48e5712 Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Sun, 22 Dec 2024 19:23:37 +0800 Subject: [PATCH 2/5] sdcard: introduce trait `Transport`, make `SdCard` an inner mutable wrapper around an implementation of `Transport` Signed-off-by: Zhouqi Jiang --- src/sdcard/mod.rs | 53 ++++++---- src/sdcard/spi.rs | 244 +++++++++++++++++++++++----------------------- 2 files changed, 154 insertions(+), 143 deletions(-) diff --git a/src/sdcard/mod.rs b/src/sdcard/mod.rs index 4623fac..b2be1d1 100644 --- a/src/sdcard/mod.rs +++ b/src/sdcard/mod.rs @@ -5,6 +5,7 @@ pub mod proto; mod spi; +pub use spi::SpiTransport; use crate::{Block, BlockCount, BlockDevice, BlockIdx}; use core::cell::RefCell; @@ -35,15 +36,11 @@ use crate::debug; /// All the APIs take `&self` - mutability is handled using an inner `RefCell`. /// /// [`SpiDevice`]: embedded_hal::spi::SpiDevice -pub struct SdCard -where - SPI: embedded_hal::spi::SpiDevice, - DELAYER: embedded_hal::delay::DelayNs, -{ - inner: RefCell>, +pub struct SdCard { + inner: RefCell, } -impl SdCard +impl SdCard> where SPI: embedded_hal::spi::SpiDevice, DELAYER: embedded_hal::delay::DelayNs, @@ -54,7 +51,7 @@ where /// deferred until a method is called on the object. /// /// Uses the default options. - pub fn new(spi: SPI, delayer: DELAYER) -> SdCard { + pub fn new(spi: SPI, delayer: DELAYER) -> Self { Self::new_with_options(spi, delayer, AcquireOpts::default()) } @@ -65,13 +62,9 @@ where /// /// The card will not be initialised at this time. Initialisation is /// deferred until a method is called on the object. - pub fn new_with_options( - spi: SPI, - delayer: DELAYER, - options: AcquireOpts, - ) -> SdCard { + pub fn new_with_options(spi: SPI, delayer: DELAYER, options: AcquireOpts) -> Self { SdCard { - inner: RefCell::new(spi::SpiSdCardInner::new(spi, delayer, options)), + inner: RefCell::new(spi::SpiTransport::new(spi, delayer, options)), } } @@ -89,7 +82,9 @@ where let mut inner = self.inner.borrow_mut(); inner.spi(func) } +} +impl SdCard { /// Return the usable size of this SD card in bytes. /// /// This will trigger card (re-)initialisation. @@ -147,11 +142,7 @@ where } } -impl BlockDevice for SdCard -where - SPI: embedded_hal::spi::SpiDevice, - DELAYER: embedded_hal::delay::DelayNs, -{ +impl BlockDevice for SdCard { type Error = Error; /// Read one or more blocks, starting at the given block index. @@ -184,6 +175,30 @@ where } } +/// Abstract SD card transportation interface. +pub trait Transport { + /// Read one or more blocks, starting at the given block index. + fn read(&mut self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Error>; + /// Write one or more blocks, starting at the given block index. + fn write(&mut self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Error>; + /// Determine how many blocks this device can hold. + fn num_blocks(&mut self) -> Result; + /// Return the usable size of this SD card in bytes. + fn num_bytes(&mut self) -> Result; + /// Can this card erase single blocks? + fn erase_single_block_enabled(&mut self) -> Result; + /// Check the card is initialised. + fn check_init(&mut self) -> Result<(), Error>; + /// Mark the card as requiring a reset. + /// + /// The next operation will assume the card has been freshly inserted. + fn mark_card_uninit(&mut self); + /// Get the card type. + fn get_card_type(&self) -> Option; + /// Tell the driver the card has been initialised. + unsafe fn mark_card_as_init(&mut self, card_type: CardType); +} + /// Options for acquiring the card. #[cfg_attr(feature = "defmt-log", derive(defmt::Format))] #[derive(Debug)] diff --git a/src/sdcard/spi.rs b/src/sdcard/spi.rs index 8bb2391..82d4c46 100644 --- a/src/sdcard/spi.rs +++ b/src/sdcard/spi.rs @@ -10,7 +10,7 @@ use crate::{debug, trace, warn}; /// Inner details for the SD Card driver. /// /// All the APIs required `&mut self`. -pub struct SpiSdCardInner +pub struct SpiTransport where SPI: embedded_hal::spi::SpiDevice, DELAYER: embedded_hal::delay::DelayNs, @@ -21,14 +21,14 @@ where options: AcquireOpts, } -impl SpiSdCardInner +impl SpiTransport where SPI: embedded_hal::spi::SpiDevice, DELAYER: embedded_hal::delay::DelayNs, { /// Construct a new raw SPI transport interface for SD/MMC Card. pub fn new(spi: SPI, delayer: DELAYER, options: AcquireOpts) -> Self { - SpiSdCardInner { + SpiTransport { spi, delayer, card_type: None, @@ -42,98 +42,6 @@ where { func(&mut self.spi) } - /// Read one or more blocks, starting at the given block index. - pub fn read(&mut self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Error> { - let start_idx = match self.card_type { - Some(CardType::SD1 | CardType::SD2) => start_block_idx.0 * 512, - Some(CardType::SDHC) => start_block_idx.0, - None => return Err(Error::CardNotFound), - }; - - if blocks.len() == 1 { - // Start a single-block read - self.card_command(CMD17, start_idx)?; - self.read_data(&mut blocks[0].contents)?; - } else { - // Start a multi-block read - self.card_command(CMD18, start_idx)?; - for block in blocks.iter_mut() { - self.read_data(&mut block.contents)?; - } - // Stop the read - self.card_command(CMD12, 0)?; - } - Ok(()) - } - - /// Write one or more blocks, starting at the given block index. - pub fn write(&mut self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Error> { - let start_idx = match self.card_type { - Some(CardType::SD1 | CardType::SD2) => start_block_idx.0 * 512, - Some(CardType::SDHC) => start_block_idx.0, - None => return Err(Error::CardNotFound), - }; - if blocks.len() == 1 { - // Start a single-block write - self.card_command(CMD24, start_idx)?; - self.write_data(DATA_START_BLOCK, &blocks[0].contents)?; - self.wait_not_busy(Delay::new_write())?; - if self.card_command(CMD13, 0)? != 0x00 { - return Err(Error::WriteError); - } - if self.read_byte()? != 0x00 { - return Err(Error::WriteError); - } - } else { - // > It is recommended using this command preceding CMD25, some of the cards will be faster for Multiple - // > Write Blocks operation. Note that the host should send ACMD23 just before WRITE command if the host - // > wants to use the pre-erased feature - self.card_acmd(ACMD23, blocks.len() as u32)?; - // wait for card to be ready before sending the next command - self.wait_not_busy(Delay::new_write())?; - - // Start a multi-block write - self.card_command(CMD25, start_idx)?; - for block in blocks.iter() { - self.wait_not_busy(Delay::new_write())?; - self.write_data(WRITE_MULTIPLE_TOKEN, &block.contents)?; - } - // Stop the write - self.wait_not_busy(Delay::new_write())?; - self.write_byte(STOP_TRAN_TOKEN)?; - } - Ok(()) - } - - /// Determine how many blocks this device can hold. - pub fn num_blocks(&mut self) -> Result { - let csd = self.read_csd()?; - debug!("CSD: {:?}", csd); - let num_blocks = match csd { - Csd::V1(ref contents) => contents.card_capacity_blocks(), - Csd::V2(ref contents) => contents.card_capacity_blocks(), - }; - Ok(BlockCount(num_blocks)) - } - - /// Return the usable size of this SD card in bytes. - pub fn num_bytes(&mut self) -> Result { - let csd = self.read_csd()?; - debug!("CSD: {:?}", csd); - match csd { - Csd::V1(ref contents) => Ok(contents.card_capacity_bytes()), - Csd::V2(ref contents) => Ok(contents.card_capacity_bytes()), - } - } - - /// Can this card erase single blocks? - pub fn erase_single_block_enabled(&mut self) -> Result { - let csd = self.read_csd()?; - match csd { - Csd::V1(ref contents) => Ok(contents.erase_single_block_enabled()), - Csd::V2(ref contents) => Ok(contents.erase_single_block_enabled()), - } - } /// Read the 'card specific data' block. fn read_csd(&mut self) -> Result { @@ -215,19 +123,8 @@ where } } - /// Check the card is initialised. - pub fn check_init(&mut self) -> Result<(), Error> { - if self.card_type.is_none() { - // If we don't know what the card type is, try and initialise the - // card. This will tell us what type of card it is. - self.acquire() - } else { - Ok(()) - } - } - /// Initializes the card into a known state (or at least tries to). - pub fn acquire(&mut self) -> Result<(), Error> { + fn acquire(&mut self) -> Result<(), Error> { debug!("acquiring card with opts: {:?}", self.options); let f = |s: &mut Self| { // Assume it hasn't worked @@ -310,23 +207,6 @@ where result } - /// Mark the card as requiring a reset. - /// - /// The next operation will assume the card has been freshly inserted. - pub fn mark_card_uninit(&mut self) { - self.card_type = None; - } - - /// Get the card type. - pub fn get_card_type(&self) -> Option { - self.card_type - } - - /// Tell the driver the card has been initialised. - pub unsafe fn mark_card_as_init(&mut self, card_type: CardType) { - self.card_type = Some(card_type); - } - /// Perform an application-specific command. fn card_acmd(&mut self, command: u8, arg: u32) -> Result { self.card_command(CMD55, 0)?; @@ -413,3 +293,119 @@ where Ok(()) } } + +impl super::Transport for SpiTransport +where + SPI: embedded_hal::spi::SpiDevice, + DELAYER: embedded_hal::delay::DelayNs, +{ + fn read(&mut self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Error> { + let start_idx = match self.card_type { + Some(CardType::SD1 | CardType::SD2) => start_block_idx.0 * 512, + Some(CardType::SDHC) => start_block_idx.0, + None => return Err(Error::CardNotFound), + }; + + if blocks.len() == 1 { + // Start a single-block read + self.card_command(CMD17, start_idx)?; + self.read_data(&mut blocks[0].contents)?; + } else { + // Start a multi-block read + self.card_command(CMD18, start_idx)?; + for block in blocks.iter_mut() { + self.read_data(&mut block.contents)?; + } + // Stop the read + self.card_command(CMD12, 0)?; + } + Ok(()) + } + + fn write(&mut self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Error> { + let start_idx = match self.card_type { + Some(CardType::SD1 | CardType::SD2) => start_block_idx.0 * 512, + Some(CardType::SDHC) => start_block_idx.0, + None => return Err(Error::CardNotFound), + }; + if blocks.len() == 1 { + // Start a single-block write + self.card_command(CMD24, start_idx)?; + self.write_data(DATA_START_BLOCK, &blocks[0].contents)?; + self.wait_not_busy(Delay::new_write())?; + if self.card_command(CMD13, 0)? != 0x00 { + return Err(Error::WriteError); + } + if self.read_byte()? != 0x00 { + return Err(Error::WriteError); + } + } else { + // > It is recommended using this command preceding CMD25, some of the cards will be faster for Multiple + // > Write Blocks operation. Note that the host should send ACMD23 just before WRITE command if the host + // > wants to use the pre-erased feature + self.card_acmd(ACMD23, blocks.len() as u32)?; + // wait for card to be ready before sending the next command + self.wait_not_busy(Delay::new_write())?; + + // Start a multi-block write + self.card_command(CMD25, start_idx)?; + for block in blocks.iter() { + self.wait_not_busy(Delay::new_write())?; + self.write_data(WRITE_MULTIPLE_TOKEN, &block.contents)?; + } + // Stop the write + self.wait_not_busy(Delay::new_write())?; + self.write_byte(STOP_TRAN_TOKEN)?; + } + Ok(()) + } + + fn num_blocks(&mut self) -> Result { + let csd = self.read_csd()?; + debug!("CSD: {:?}", csd); + let num_blocks = match csd { + Csd::V1(ref contents) => contents.card_capacity_blocks(), + Csd::V2(ref contents) => contents.card_capacity_blocks(), + }; + Ok(BlockCount(num_blocks)) + } + + fn num_bytes(&mut self) -> Result { + let csd = self.read_csd()?; + debug!("CSD: {:?}", csd); + match csd { + Csd::V1(ref contents) => Ok(contents.card_capacity_bytes()), + Csd::V2(ref contents) => Ok(contents.card_capacity_bytes()), + } + } + + fn erase_single_block_enabled(&mut self) -> Result { + let csd = self.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_init(&mut self) -> Result<(), Error> { + if self.card_type.is_none() { + // If we don't know what the card type is, try and initialise the + // card. This will tell us what type of card it is. + self.acquire() + } else { + Ok(()) + } + } + + fn mark_card_uninit(&mut self) { + self.card_type = None; + } + + fn get_card_type(&self) -> Option { + self.card_type + } + + unsafe fn mark_card_as_init(&mut self, card_type: CardType) { + self.card_type = Some(card_type); + } +} From 0fc8893e9046252ada8141c30f5a40604f347e85 Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Sun, 22 Dec 2024 19:28:23 +0800 Subject: [PATCH 3/5] sdcard!: rename SdCard constructors to `new_spi` and `new_spi_with_options` Signed-off-by: Zhouqi Jiang --- CHANGELOG.md | 1 + README.md | 2 +- examples/readme_test.rs | 2 +- src/lib.rs | 2 +- src/sdcard/mod.rs | 6 +++--- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a5e92e..78998f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog] and this project adheres to [Semantic - __Breaking Change__: The `VolumeManager::device` method now takes a callback rather than giving you a reference to the underlying `BlockDevice` - __Breaking Change__: `Error:LockError` variant added. - __Breaking Change__: `SearchId` was renamed to `Handle` +- __Breaking Change__: sdcard!: add `Transport` trait, make `SdCard` an abstract of `Transport` other than `SpiDevice`, refactor internal implementation, add `new_spi` and `new_spi_with_options` functions. ### Added diff --git a/README.md b/README.md index eace6f8..ab1de0e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ You will need something that implements the `BlockDevice` trait, which can read ```rust // Build an SD Card interface out of an SPI device, a chip-select pin and the delay object -let sdcard = embedded_sdmmc::SdCard::new(sdmmc_spi, delay); +let sdcard = embedded_sdmmc::SdCard::new_spi(sdmmc_spi, delay); // Get the card size (this also triggers card initialisation because it's not been done yet) println!("Card size is {} bytes", sdcard.num_bytes()?); // Now let's look for volumes (also known as partitions) on our block device. diff --git a/examples/readme_test.rs b/examples/readme_test.rs index fd86780..aad9922 100644 --- a/examples/readme_test.rs +++ b/examples/readme_test.rs @@ -120,7 +120,7 @@ fn main() -> Result<(), Error> { // END Fake stuff that will be replaced with real peripherals // Build an SD Card interface out of an SPI device, a chip-select pin and the delay object - let sdcard = embedded_sdmmc::SdCard::new(sdmmc_spi, delay); + let sdcard = embedded_sdmmc::SdCard::new_spi(sdmmc_spi, delay); // Get the card size (this also triggers card initialisation because it's not been done yet) println!("Card size is {} bytes", sdcard.num_bytes()?); // Now let's look for volumes (also known as partitions) on our block device. diff --git a/src/lib.rs b/src/lib.rs index 853a69b..d45b3aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ //! D: embedded_hal::delay::DelayNs, //! T: TimeSource, //! { -//! let sdcard = SdCard::new(spi, delay); +//! let sdcard = SdCard::new_spi(spi, delay); //! println!("Card size is {} bytes", sdcard.num_bytes()?); //! let volume_mgr = VolumeManager::new(sdcard, ts); //! let volume0 = volume_mgr.open_volume(VolumeIdx(0))?; diff --git a/src/sdcard/mod.rs b/src/sdcard/mod.rs index b2be1d1..3110674 100644 --- a/src/sdcard/mod.rs +++ b/src/sdcard/mod.rs @@ -51,8 +51,8 @@ where /// deferred until a method is called on the object. /// /// Uses the default options. - pub fn new(spi: SPI, delayer: DELAYER) -> Self { - Self::new_with_options(spi, delayer, AcquireOpts::default()) + pub fn new_spi(spi: SPI, delayer: DELAYER) -> Self { + Self::new_spi_with_options(spi, delayer, AcquireOpts::default()) } /// Construct a new SD/MMC Card driver, using a raw SPI interface and the given options. @@ -62,7 +62,7 @@ where /// /// The card will not be initialised at this time. Initialisation is /// deferred until a method is called on the object. - pub fn new_with_options(spi: SPI, delayer: DELAYER, options: AcquireOpts) -> Self { + pub fn new_spi_with_options(spi: SPI, delayer: DELAYER, options: AcquireOpts) -> Self { SdCard { inner: RefCell::new(spi::SpiTransport::new(spi, delayer, options)), } From 5f4b3e7f0e9824e3615bd02b0df871293cd4470c Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Sun, 22 Dec 2024 19:43:54 +0800 Subject: [PATCH 4/5] sdcard: add `SdCard::new` function, small document fixes Signed-off-by: Zhouqi Jiang --- src/sdcard/mod.rs | 9 +++++++++ src/sdcard/spi.rs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sdcard/mod.rs b/src/sdcard/mod.rs index 3110674..e59c0bd 100644 --- a/src/sdcard/mod.rs +++ b/src/sdcard/mod.rs @@ -40,6 +40,15 @@ pub struct SdCard { inner: RefCell, } +impl SdCard { + /// Create a new SD/MMC Card driver using an abstract transport interface. + pub fn new(transport: T) -> Self { + Self { + inner: RefCell::new(transport), + } + } +} + impl SdCard> where SPI: embedded_hal::spi::SpiDevice, diff --git a/src/sdcard/spi.rs b/src/sdcard/spi.rs index 82d4c46..a7846c0 100644 --- a/src/sdcard/spi.rs +++ b/src/sdcard/spi.rs @@ -7,7 +7,7 @@ use super::{proto::*, AcquireOpts, CardType, Delay, Error}; use crate::blockdevice::{Block, BlockCount, BlockIdx}; use crate::{debug, trace, warn}; -/// Inner details for the SD Card driver. +/// SPI transportation for the SD Card driver. /// /// All the APIs required `&mut self`. pub struct SpiTransport From d16739e1b9e1d6407bf66f02d35a8504a146e99b Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 22 Dec 2024 15:05:43 +0000 Subject: [PATCH 5/5] Updated the README and added minor typo fixes --- README.md | 26 +++++++++++++++++--------- src/lib.rs | 5 +---- src/sdcard/mod.rs | 37 +++++++++++++++++++++---------------- src/sdcard/spi.rs | 15 ++++++++++++--- 4 files changed, 51 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index ab1de0e..c7d1170 100644 --- a/README.md +++ b/README.md @@ -46,17 +46,25 @@ By default the `VolumeManager` will initialize with a maximum number of `4` open let cont: VolumeManager<_, _, 6, 12, 4> = VolumeManager::new_with_limits(block, time_source); ``` +### Accessing SD Cards using an SD Host Controller + +The `SdCard` type requires something that implements `Transport` in order to speak to the SD Card. We supply a generic `SpiTransport` that implements `Transport` using some underlying user-supplied implementation of `embedded_hal::spi::SpiDevice`. That will work for most applications. + +However, if your MCU has a full SD Host Controller peripheral, and if you need high-performance access to your SD Card, you may wish to instead implement `Transport` in a way that uses that peripheral. SD Host Controllers, for example, often support a 4-bit wide interface instead of the 1-bit wide SPI interface, and run at higher clock rates. + ## Supported features -* Open files in all supported methods from an open directory -* Open an arbitrary number of directories and files -* Read data from open files -* Write data to open files -* Close files -* Delete files -* Iterate root directory -* Iterate sub-directories -* Log over defmt or the common log interface (feature flags). +* Talking to SD Cards over SPI (using the `embedded-hal::spi::SpiDevice` trait) +* Talking to SD Cards over a custom transport (using our `Transport` trait) +* Opening files in all supported modes from an open directory +* Opening an arbitrary number of directories and files +* Reading data from open files +* Writing data to open files +* Closing files +* Deleting files +* Iterating the root directory +* Iterating sub-directories +* Logging over defmt or the common log interface (see feature flags). ## No-std usage diff --git a/src/lib.rs b/src/lib.rs index d45b3aa..e4dd6b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,10 +92,7 @@ pub use crate::filesystem::{ use filesystem::DirectoryInfo; #[doc(inline)] -pub use crate::sdcard::Error as SdCardError; - -#[doc(inline)] -pub use crate::sdcard::SdCard; +pub use crate::sdcard::{Error as SdCardError, SdCard, SpiTransport, Transport}; mod volume_mgr; #[doc(inline)] diff --git a/src/sdcard/mod.rs b/src/sdcard/mod.rs index e59c0bd..04d84d8 100644 --- a/src/sdcard/mod.rs +++ b/src/sdcard/mod.rs @@ -5,6 +5,8 @@ pub mod proto; mod spi; + +#[doc(inline)] pub use spi::SpiTransport; use crate::{Block, BlockCount, BlockDevice, BlockIdx}; @@ -20,18 +22,7 @@ use crate::debug; // Types and Implementations // **************************************************************************** -/// Driver for an SD Card on an SPI bus. -/// -/// Built from an [`SpiDevice`] implementation and a Chip Select pin. -/// -/// Before talking to the SD Card, the caller needs to send 74 clocks cycles on -/// the SPI Clock line, at 400 kHz, with no chip-select asserted (or at least, -/// not the chip-select of the SD Card). -/// -/// This kind of breaks the embedded-hal model, so how to do this is left to -/// the caller. You could drive the SpiBus directly, or use an SpiDevice with -/// a dummy chip-select pin. Or you could try just not doing the 74 clocks and -/// see if your card works anyway - some do, some don't. +/// Driver for an SD Card using some generic SD Card [`Transport`]. /// /// All the APIs take `&self` - mutability is handled using an inner `RefCell`. /// @@ -56,6 +47,12 @@ where { /// Create a new SD/MMC Card driver using a raw SPI interface. /// + /// This is just a short-cut method to provide backwards compatibility with + /// our old API. + /// + /// It creates an [`SpiTransport`] for you, so refer to the documentation + /// there for important details. + /// /// The card will not be initialised at this time. Initialisation is /// deferred until a method is called on the object. /// @@ -64,10 +61,14 @@ where Self::new_spi_with_options(spi, delayer, AcquireOpts::default()) } - /// Construct a new SD/MMC Card driver, using a raw SPI interface and the given options. + /// Construct a new SD/MMC Card driver, using a raw SPI interface and the + /// given options. /// - /// See the docs of the [`SdCard`] struct for more information about - /// how to construct the needed `SPI` and `CS` types. + /// This is just a short-cut method to provide backwards compatibility with + /// our old API. + /// + /// It creates an [`SpiTransport`] for you, so refer to the documentation + /// there for important details. /// /// The card will not be initialised at this time. Initialisation is /// deferred until a method is called on the object. @@ -184,7 +185,11 @@ impl BlockDevice for SdCard { } } -/// Abstract SD card transportation interface. +/// Abstract SD Card Transport interface. +/// +/// We implement this trait to produce an SPI based transport over in +/// [`SpiTransport`]. You can implement it yourself to support your favourite SD +/// Host Controller if you prefer. pub trait Transport { /// Read one or more blocks, starting at the given block index. fn read(&mut self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Error>; diff --git a/src/sdcard/spi.rs b/src/sdcard/spi.rs index a7846c0..b6e98d4 100644 --- a/src/sdcard/spi.rs +++ b/src/sdcard/spi.rs @@ -1,4 +1,4 @@ -//! Implements the BlockDevice trait for an SD/MMC Protocol over SPI. +//! Implements the [`Transport`] trait for speaking SD/MMC Protocol over SPI. //! //! This is currently optimised for readability and debugability, not //! performance. @@ -7,9 +7,9 @@ use super::{proto::*, AcquireOpts, CardType, Delay, Error}; use crate::blockdevice::{Block, BlockCount, BlockIdx}; use crate::{debug, trace, warn}; -/// SPI transportation for the SD Card driver. +/// An SPI-based implementation of [`Transport`](crate::Transport). /// -/// All the APIs required `&mut self`. +/// All the APIs require `&mut self`. pub struct SpiTransport where SPI: embedded_hal::spi::SpiDevice, @@ -27,6 +27,15 @@ where DELAYER: embedded_hal::delay::DelayNs, { /// Construct a new raw SPI transport interface for SD/MMC Card. + /// + /// Before talking to the SD Card, the caller needs to send 74 clocks cycles + /// on the SPI Clock line, at 400 kHz, with no chip-select asserted (or at + /// least, not the chip-select of the SD Card). + /// + /// This kind of breaks the embedded-hal model, so how to do this is left to + /// the caller. You could drive the SpiBus directly, or use an SpiDevice + /// with a dummy chip-select pin. Or you could try just not doing the 74 + /// clocks and see if your card works anyway - some do, some don't. pub fn new(spi: SPI, delayer: DELAYER, options: AcquireOpts) -> Self { SpiTransport { spi,