#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
use crate::dma::Channel;
use crate::emmc::clock_divider;
use crate::mailbox::Mailbox;
#[cfg(not(feature = "bcm2712"))]
use crate::mailbox::{ClockId, PowerDeviceId};
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
use crate::pac::EMMC;
use crate::pac::GPIO;
use crate::timer::Timer;
#[cfg(all(feature = "async", not(any(feature = "bcm2711", feature = "bcm2712"))))]
mod asynch;
#[cfg(all(feature = "async", not(any(feature = "bcm2711", feature = "bcm2712"))))]
pub use asynch::on_irq;
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
const GPIO_CLK: u32 = 48;
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
const GPIO_CMD: u32 = 49;
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
const GPIO_DAT: [u32; 4] = [50, 51, 52, 53];
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
const GPIO_ALT_FUNCTION: u8 = 0b111;
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
const GPIO_INPUT: u8 = 0b000;
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
const EMMC_CLOCK_ID: ClockId = ClockId::Emmc;
#[cfg(all(feature = "bcm2711", not(feature = "bcm2712")))]
const EMMC_CLOCK_ID: ClockId = ClockId::Emmc2;
#[cfg(feature = "bcm2712")]
const BCM2712_BASE_CLOCK_HZ: u32 = 200_000_000;
#[cfg(all(feature = "bcm2711", not(feature = "bcm2712")))]
pub struct Emmc2 {
_marker: core::marker::PhantomData<*const ()>,
}
#[cfg(all(feature = "bcm2711", not(feature = "bcm2712")))]
unsafe impl Send for Emmc2 {}
#[cfg(all(feature = "bcm2711", not(feature = "bcm2712")))]
impl Emmc2 {
const PTR: *const crate::pac::emmc::RegisterBlock = 0xfe34_0000 as *const _;
pub fn new() -> Self {
Self {
_marker: core::marker::PhantomData,
}
}
}
#[cfg(all(feature = "bcm2711", not(feature = "bcm2712")))]
impl Default for Emmc2 {
fn default() -> Self {
Self::new()
}
}
#[cfg(all(feature = "bcm2711", not(feature = "bcm2712")))]
impl core::ops::Deref for Emmc2 {
type Target = crate::pac::emmc::RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*Self::PTR }
}
}
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
type SdEmmc = EMMC;
#[cfg(all(feature = "bcm2711", not(feature = "bcm2712")))]
type SdEmmc = Emmc2;
#[cfg(feature = "bcm2712")]
type SdEmmc = crate::pac::SDIO1;
const SETUP_CLOCK_HZ: u32 = 400_000;
const TRANSFER_CLOCK_HZ: u32 = 25_000_000;
const DATA_TIMEOUT_MAX: u8 = 0b1110;
const CMD_GO_IDLE: u32 = 0x0000_0000;
const CMD_ALL_SEND_CID: u32 = 0x0201_0000;
const CMD_SEND_REL_ADDR: u32 = 0x0302_0000;
const CMD_CARD_SELECT: u32 = 0x0703_0000;
const CMD_SEND_IF_COND: u32 = 0x0802_0000;
const CMD_READ_SINGLE: u32 = 0x1122_0010;
const CMD_WRITE_SINGLE: u32 = 0x1822_0000;
const CMD_READ_MULTI: u32 = 0x1222_0036;
const CMD_WRITE_MULTI: u32 = 0x1922_0026;
const CMD_APP_CMD: u32 = 0x370A_0000;
const CMD_SEND_OP_COND: u32 = 0x2902_0000 | CMD_NEED_APP;
const CMD_SEND_SCR: u32 = 0x3322_0010 | CMD_NEED_APP;
const CMD_SET_BUS_WIDTH: u32 = 0x0602_0000 | CMD_NEED_APP;
const CMD_NEED_APP: u32 = 0x8000_0000;
const BUS_WIDTH_4BIT: u32 = 2;
const INT_ERROR_MASK: u32 = 0x017e_8000;
const INT_ERR_SUMMARY: u32 = 0x0000_8000;
const INT_CMD_TIMEOUT: u32 = 0x0001_0000;
const INT_CMD_DONE: u32 = 0x0000_0001;
const INT_DATA_DONE: u32 = 0x0000_0002;
const INT_WRITE_RDY: u32 = 0x0000_0010;
const INT_READ_RDY: u32 = 0x0000_0020;
const ACMD41_ARG_HC: u32 = 0x51ff_8000;
const ACMD41_CMD_COMPLETE: u32 = 0x8000_0000;
const ACMD41_VOLTAGE: u32 = 0x00ff_8000;
const ACMD41_CMD_CCS: u32 = 0x4000_0000;
const SCR_SD_BUS_WIDTH_4: u32 = 0x0000_0400;
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
const DMA_DREQ_EMMC: u8 = 11;
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
const DATA_REG_BUS_ADDRESS: u32 = 0x7e30_0020;
pub type Block = [u8; 512];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
NoCard,
Timeout,
WaitTimeout {
waiting_for: u32,
interrupt: u32,
status: u32,
command: u32,
},
CardError {
interrupt: u32,
command: u32,
},
UnusableCard,
PowerOnFailed,
TooManyBlocks,
#[cfg(not(feature = "bcm2712"))]
Dma(crate::dma::Error),
LinesNotIdleHigh {
cmd_line_high: bool,
data_lines_high: u8,
},
}
pub struct Sd {
emmc: SdEmmc,
high_capacity: bool,
rca: u32,
four_bit_bus: bool,
}
impl Sd {
pub unsafe fn steal_emmc() -> SdEmmc {
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
{
unsafe { crate::pac::Peripherals::steal() }.EMMC
}
#[cfg(all(feature = "bcm2711", not(feature = "bcm2712")))]
{
Emmc2::new()
}
#[cfg(feature = "bcm2712")]
{
unsafe { crate::pac::SDIO1::steal() }
}
}
pub fn init(
gpio: &GPIO,
emmc: SdEmmc,
mailbox: &mut Mailbox,
timer: &Timer,
) -> Result<Self, Error> {
#[cfg(not(feature = "bcm2712"))]
if !matches!(
mailbox.set_power_state(PowerDeviceId::SdCard, true),
Ok(true)
) {
return Err(Error::PowerOnFailed);
}
#[cfg(feature = "bcm2712")]
{
let _ = mailbox;
bcm2712_power_cycle_card(timer);
bcm2712_configure_host();
}
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
route_gpio_to_emmc(gpio);
#[cfg(any(feature = "bcm2711", feature = "bcm2712"))]
let _ = gpio;
timer.delay_ms(1);
let status = emmc.status().read();
let (cmd_line_high, data_lines_high) =
(status.cmd_level().bit_is_set(), status.dat_level0().bits());
if !cmd_line_high || data_lines_high != 0b1111 {
return Err(Error::LinesNotIdleHigh {
cmd_line_high,
data_lines_high,
});
}
#[cfg(all(feature = "bcm2711", not(feature = "bcm2712")))]
mailbox
.set_clock_state(EMMC_CLOCK_ID, true)
.map_err(|_| Error::PowerOnFailed)?;
#[cfg(not(feature = "bcm2712"))]
let base_clock_hz = mailbox
.clock_rate_hz(EMMC_CLOCK_ID)
.map_err(|_| Error::PowerOnFailed)?;
#[cfg(feature = "bcm2712")]
let base_clock_hz = BCM2712_BASE_CLOCK_HZ;
emmc.control0().reset();
emmc.control1().modify(|_, w| w.srst_hc().set_bit());
wait_for(timer, 100_000, || {
!emmc.control1().read().srst_hc().bit_is_set()
})?;
#[cfg(any(feature = "bcm2711", feature = "bcm2712"))]
emmc.control0()
.modify(|r, w| unsafe { w.bits(r.bits() | 0x0f00) });
emmc.control1().modify(|_, w| unsafe {
w.clk_intlen()
.set_bit()
.data_tounit()
.bits(DATA_TIMEOUT_MAX)
});
timer.delay_ms(10);
set_clock(&emmc, base_clock_hz, SETUP_CLOCK_HZ, timer)?;
emmc.irpt_mask().write(|w| unsafe { w.bits(0xffff_ffff) });
let sd = Self {
emmc,
high_capacity: false,
rca: 0,
four_bit_bus: false,
};
sd.command(CMD_GO_IDLE, 0, timer)?;
if let Err(error) = sd.command(CMD_SEND_IF_COND, 0x0000_01aa, timer) {
return Err(sd.diagnose_empty_slot(error, timer));
}
let start = timer.now_micros();
let response = loop {
let response = sd.command(CMD_SEND_OP_COND, ACMD41_ARG_HC, timer)?;
if response & ACMD41_CMD_COMPLETE != 0 {
break response;
}
if timer.now_micros() - start > 1_000_000 {
return Err(Error::Timeout);
}
timer.delay_ms(10);
};
if response & ACMD41_VOLTAGE == 0 {
return Err(Error::UnusableCard);
}
let high_capacity = response & ACMD41_CMD_CCS != 0;
sd.command(CMD_ALL_SEND_CID, 0, timer)?;
let rca = sd.command(CMD_SEND_REL_ADDR, 0, timer)? & 0xffff_0000;
let sd = Self { rca, ..sd };
set_clock(&sd.emmc, base_clock_hz, TRANSFER_CLOCK_HZ, timer)?;
sd.command(CMD_CARD_SELECT, rca, timer)?;
let four_bit_bus = sd.negotiate_four_bit_bus(timer).unwrap_or(false);
Ok(Self {
high_capacity,
four_bit_bus,
..sd
})
}
pub fn high_capacity(&self) -> bool {
self.high_capacity
}
pub fn four_bit_bus(&self) -> bool {
self.four_bit_bus
}
pub fn read_block(
&self,
block_index: u32,
buf: &mut Block,
timer: &Timer,
) -> Result<(), Error> {
self.read_blocks_pio(block_index, 1, core::iter::once(buf), timer)
}
pub fn read_blocks(
&self,
block_index: u32,
blocks: &mut [Block],
timer: &Timer,
) -> Result<(), Error> {
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
self.read_blocks_pio(block_index, count, blocks.iter_mut(), timer)
}
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
pub fn read_blocks_dma(
&self,
block_index: u32,
blocks: &mut [Block],
channel: &mut Channel,
timer: &Timer,
) -> Result<(), Error> {
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
let cmd = if count == 1 {
CMD_READ_SINGLE
} else {
CMD_READ_MULTI
};
self.start_transfer(cmd, block_index, count, timer)?;
channel
.copy_from_peripheral(
blocks.as_flattened_mut(),
DMA_DREQ_EMMC,
DATA_REG_BUS_ADDRESS,
)
.map_err(Error::Dma)?;
self.wait_interrupt(INT_DATA_DONE, cmd, timer)?;
Ok(())
}
pub fn write_block(&self, block_index: u32, buf: &Block, timer: &Timer) -> Result<(), Error> {
self.write_blocks_pio(block_index, 1, core::iter::once(buf), timer)
}
pub fn write_blocks(
&self,
block_index: u32,
blocks: &[Block],
timer: &Timer,
) -> Result<(), Error> {
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
self.write_blocks_pio(block_index, count, blocks.iter(), timer)
}
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
pub fn write_blocks_dma(
&self,
block_index: u32,
blocks: &[Block],
channel: &mut Channel,
timer: &Timer,
) -> Result<(), Error> {
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
let cmd = if count == 1 {
CMD_WRITE_SINGLE
} else {
CMD_WRITE_MULTI
};
self.start_transfer(cmd, block_index, count, timer)?;
channel
.copy_to_peripheral(blocks.as_flattened(), DMA_DREQ_EMMC, DATA_REG_BUS_ADDRESS)
.map_err(Error::Dma)?;
self.wait_interrupt(INT_DATA_DONE, cmd, timer)?;
Ok(())
}
fn read_blocks_pio<'a>(
&self,
block_index: u32,
count: u16,
blocks: impl Iterator<Item = &'a mut Block>,
timer: &Timer,
) -> Result<(), Error> {
let cmd = if count == 1 {
CMD_READ_SINGLE
} else {
CMD_READ_MULTI
};
self.start_transfer(cmd, block_index, count, timer)?;
for block in blocks {
self.wait_interrupt(INT_READ_RDY, cmd, timer)?;
for chunk in block.as_chunks_mut::<4>().0 {
*chunk = self.emmc.data().read().bits().to_le_bytes();
}
}
if count > 1 {
self.wait_interrupt(INT_DATA_DONE, cmd, timer)?;
}
Ok(())
}
fn write_blocks_pio<'a>(
&self,
block_index: u32,
count: u16,
blocks: impl Iterator<Item = &'a Block>,
timer: &Timer,
) -> Result<(), Error> {
let cmd = if count == 1 {
CMD_WRITE_SINGLE
} else {
CMD_WRITE_MULTI
};
self.start_transfer(cmd, block_index, count, timer)?;
for block in blocks {
self.wait_interrupt(INT_WRITE_RDY, cmd, timer)?;
for &chunk in block.as_chunks::<4>().0 {
self.emmc
.data()
.write(|w| unsafe { w.bits(u32::from_le_bytes(chunk)) });
}
}
self.wait_interrupt(INT_DATA_DONE, cmd, timer)?;
Ok(())
}
fn start_transfer(
&self,
cmd: u32,
block_index: u32,
block_count: u16,
timer: &Timer,
) -> Result<(), Error> {
self.wait_data_ready(timer)?;
self.emmc
.blksizecnt()
.write(|w| unsafe { w.blksize().bits(512).blkcnt().bits(block_count) });
let arg = if self.high_capacity {
block_index
} else {
block_index * 512
};
self.command(cmd, arg, timer).map(|_| ())
}
fn negotiate_four_bit_bus(&self, timer: &Timer) -> Result<bool, Error> {
self.wait_data_ready(timer)?;
self.emmc
.blksizecnt()
.write(|w| unsafe { w.blksize().bits(8).blkcnt().bits(1) });
self.command(CMD_SEND_SCR, 0, timer)?;
self.wait_interrupt(INT_READ_RDY, CMD_SEND_SCR, timer)?;
let mut scr = [0u32; 2];
for word in &mut scr {
*word = self.emmc.data().read().bits();
}
if scr[0] & SCR_SD_BUS_WIDTH_4 == 0 {
return Ok(false);
}
self.command(CMD_SET_BUS_WIDTH, BUS_WIDTH_4BIT, timer)?;
self.emmc
.control0()
.modify(|_, w| w.hctl_dwidth().set_bit());
Ok(true)
}
fn wait_data_ready(&self, timer: &Timer) -> Result<(), Error> {
wait_for(timer, 100_000, || {
self.emmc.status().read().dat_inhibit().bit_is_clear()
})
}
fn diagnose_empty_slot(&self, error: Error, timer: &Timer) -> Error {
let Error::CardError { interrupt, .. } = error else {
return error;
};
if interrupt & INT_CMD_TIMEOUT == 0 || interrupt & INT_ERROR_MASK != INT_ERR_SUMMARY {
return error;
}
self.emmc.control1().modify(|_, w| w.srst_cmd().set_bit());
if wait_for(timer, 10_000, || {
!self.emmc.control1().read().srst_cmd().bit_is_set()
})
.is_err()
{
return error;
}
match self.command(CMD_APP_CMD, 0, timer) {
Err(Error::CardError { interrupt, .. }) if interrupt & INT_CMD_TIMEOUT != 0 => {
Error::NoCard
}
_ => error,
}
}
fn command(&self, code: u32, arg: u32, timer: &Timer) -> Result<u32, Error> {
if code & CMD_NEED_APP != 0 {
self.command(CMD_APP_CMD, self.rca, timer)?;
}
let code = code & !CMD_NEED_APP;
wait_for(timer, 100_000, || {
self.emmc.status().read().cmd_inhibit().bit_is_clear()
})?;
let stale = self.emmc.interrupt().read().bits();
self.emmc.interrupt().write(|w| unsafe { w.bits(stale) });
self.emmc.arg1().write(|w| unsafe { w.bits(arg) });
self.emmc.cmdtm().write(|w| unsafe { w.bits(code) });
self.wait_interrupt(INT_CMD_DONE, code, timer)?;
Ok(self.emmc.resp0().read().bits())
}
fn wait_interrupt(&self, mask: u32, command: u32, timer: &Timer) -> Result<u32, Error> {
let start = timer.now_micros();
loop {
if let Some(result) = self.poll_interrupt(mask, command) {
return result;
}
if timer.now_micros() - start > 1_000_000 {
return Err(Error::WaitTimeout {
waiting_for: mask,
interrupt: self.emmc.interrupt().read().bits(),
status: self.emmc.status().read().bits(),
command,
});
}
}
}
fn poll_interrupt(&self, mask: u32, command: u32) -> Option<Result<u32, Error>> {
let full_mask = mask | INT_ERROR_MASK;
let interrupt = self.emmc.interrupt().read().bits();
if interrupt & full_mask == 0 {
return None;
}
self.emmc
.interrupt()
.write(|w| unsafe { w.bits(interrupt & full_mask) });
if interrupt & INT_ERROR_MASK != 0 {
return Some(Err(Error::CardError { interrupt, command }));
}
Some(Ok(interrupt))
}
}
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
fn route_gpio_to_emmc(gpio: &GPIO) {
gpio.gpfsel3().modify(|_, w| {
w.fsel34()
.bits(GPIO_INPUT)
.fsel35()
.bits(GPIO_INPUT)
.fsel36()
.bits(GPIO_INPUT)
.fsel37()
.bits(GPIO_INPUT)
.fsel38()
.bits(GPIO_INPUT)
.fsel39()
.bits(GPIO_INPUT)
});
gpio.gpfsel4().modify(|_, w| {
w.fsel48()
.bits(GPIO_ALT_FUNCTION)
.fsel49()
.bits(GPIO_ALT_FUNCTION)
});
gpio.gpfsel5().modify(|_, w| {
w.fsel50()
.bits(GPIO_ALT_FUNCTION)
.fsel51()
.bits(GPIO_ALT_FUNCTION)
.fsel52()
.bits(GPIO_ALT_FUNCTION)
.fsel53()
.bits(GPIO_ALT_FUNCTION)
});
set_emmc_pull_up(gpio);
}
#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
fn set_emmc_pull_up(gpio: &GPIO) {
let mask = (1 << (GPIO_CLK - 32))
| (1 << (GPIO_CMD - 32))
| GPIO_DAT
.iter()
.fold(0, |mask, pin| mask | (1 << (pin - 32)));
crate::gpio::set_pull_bank(gpio, 1, mask, crate::gpio::Pull::Up);
}
#[cfg(feature = "bcm2712")]
fn bcm2712_power_cycle_card(timer: &Timer) {
const SD_IO_1V8: u8 = 3;
const SD_VCC: u8 = 4;
crate::gio_aon::drive(SD_VCC, false);
crate::gio_aon::drive(SD_IO_1V8, false);
timer.delay_ms(50);
crate::gio_aon::drive(SD_VCC, true);
timer.delay_ms(10);
}
#[cfg(feature = "bcm2712")]
fn bcm2712_configure_host() {
const SDIO_CFG_CTRL: *mut u32 = crate::pac::SDIO1::CFG as *mut u32;
const SD_PIN_SEL: *mut u32 = (crate::pac::SDIO1::CFG + 0x44) as *mut u32;
const SDCD_N_TEST_EN: u32 = 1 << 31;
const SDCD_N_TEST_LEV: u32 = 1 << 30;
const PIN_SEL_MASK: u32 = 0b11;
const PIN_SEL_SD: u32 = 1 << 1;
unsafe {
let pin_sel = SD_PIN_SEL.read_volatile() & !PIN_SEL_MASK;
SD_PIN_SEL.write_volatile(pin_sel | PIN_SEL_SD);
let ctrl = SDIO_CFG_CTRL.read_volatile() & !SDCD_N_TEST_LEV;
SDIO_CFG_CTRL.write_volatile(ctrl | SDCD_N_TEST_EN);
}
}
fn set_clock(emmc: &SdEmmc, base_hz: u32, target_hz: u32, timer: &Timer) -> Result<(), Error> {
wait_for(timer, 100_000, || {
let status = emmc.status().read();
status.cmd_inhibit().bit_is_clear() && status.dat_inhibit().bit_is_clear()
})?;
emmc.control1().modify(|_, w| w.clk_en().clear_bit());
timer.delay_ms(10);
let (freq8, freq_ms2) = clock_divider(base_hz, target_hz);
emmc.control1()
.modify(|_, w| unsafe { w.clk_freq8().bits(freq8).clk_freq_ms2().bits(freq_ms2) });
timer.delay_ms(10);
emmc.control1().modify(|_, w| w.clk_en().set_bit());
timer.delay_ms(10);
wait_for(timer, 100_000, || {
emmc.control1().read().clk_stable().bit_is_set()
})
}
fn checked_block_count(len: usize) -> Result<u16, Error> {
u16::try_from(len).map_err(|_| Error::TooManyBlocks)
}
fn wait_for(
timer: &Timer,
timeout_us: u64,
mut condition: impl FnMut() -> bool,
) -> Result<(), Error> {
let start = timer.now_micros();
loop {
if condition() {
return Ok(());
}
if timer.now_micros() - start > timeout_us {
return Err(Error::Timeout);
}
}
}
#[cfg(feature = "embedded-sdmmc")]
pub struct SdCard<'t> {
sd: Sd,
timer: &'t Timer,
}
#[cfg(feature = "embedded-sdmmc")]
impl<'t> SdCard<'t> {
pub fn new(sd: Sd, timer: &'t Timer) -> Self {
Self { sd, timer }
}
}
#[cfg(feature = "embedded-sdmmc")]
#[derive(Debug)]
pub enum SdCardError {
Sd(Error),
Unsupported,
}
#[cfg(feature = "embedded-sdmmc")]
impl From<Error> for SdCardError {
fn from(e: Error) -> Self {
SdCardError::Sd(e)
}
}
#[cfg(feature = "embedded-sdmmc")]
impl embedded_sdmmc::BlockDevice for SdCard<'_> {
type Error = SdCardError;
fn read(
&self,
blocks: &mut [embedded_sdmmc::Block],
start_block_idx: embedded_sdmmc::BlockIdx,
) -> Result<(), Self::Error> {
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
self.sd.read_blocks_pio(
start_block_idx.0,
count,
blocks.iter_mut().map(|block| &mut block.contents),
self.timer,
)?;
Ok(())
}
fn write(
&self,
blocks: &[embedded_sdmmc::Block],
start_block_idx: embedded_sdmmc::BlockIdx,
) -> Result<(), Self::Error> {
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
self.sd.write_blocks_pio(
start_block_idx.0,
count,
blocks.iter().map(|block| &block.contents),
self.timer,
)?;
Ok(())
}
fn num_blocks(&self) -> Result<embedded_sdmmc::BlockCount, Self::Error> {
Err(SdCardError::Unsupported)
}
}
#[cfg(feature = "resident-fat")]
const BLOCK_LEN: usize = resident_fat::BLOCK_SIZE;
#[cfg(feature = "resident-fat")]
const _: () = assert!(BLOCK_LEN == core::mem::size_of::<Block>());
#[cfg(feature = "resident-fat")]
pub struct SdBlockDevice<'t> {
sd: Sd,
timer: &'t Timer,
}
#[cfg(feature = "resident-fat")]
impl<'t> SdBlockDevice<'t> {
pub fn new(sd: Sd, timer: &'t Timer) -> Self {
Self { sd, timer }
}
pub fn inner(&self) -> &Sd {
&self.sd
}
pub fn into_inner(self) -> Sd {
self.sd
}
}
#[cfg(feature = "resident-fat")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SdBlockDeviceError {
Sd(Error),
BlockOutOfRange {
start_block: u64,
},
}
#[cfg(feature = "resident-fat")]
impl From<Error> for SdBlockDeviceError {
fn from(e: Error) -> Self {
SdBlockDeviceError::Sd(e)
}
}
#[cfg(feature = "resident-fat")]
fn checked_block_index(start_block: u64) -> Result<u32, SdBlockDeviceError> {
u32::try_from(start_block).map_err(|_| SdBlockDeviceError::BlockOutOfRange { start_block })
}
#[cfg(feature = "resident-fat")]
impl resident_fat::BlockDevice for SdBlockDevice<'_> {
type Error = SdBlockDeviceError;
fn read(&mut self, start_block: u64, blocks: &mut [u8]) -> Result<(), Self::Error> {
let index = checked_block_index(start_block)?;
let (blocks, rest) = blocks.as_chunks_mut::<BLOCK_LEN>();
assert!(rest.is_empty(), "transfer length must be a multiple of 512");
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
self.sd
.read_blocks_pio(index, count, blocks.iter_mut(), self.timer)?;
Ok(())
}
fn write(&mut self, start_block: u64, blocks: &[u8]) -> Result<(), Self::Error> {
let index = checked_block_index(start_block)?;
let (blocks, rest) = blocks.as_chunks::<BLOCK_LEN>();
assert!(rest.is_empty(), "transfer length must be a multiple of 512");
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
self.sd
.write_blocks_pio(index, count, blocks.iter(), self.timer)?;
Ok(())
}
fn block_count(&mut self) -> Result<Option<u64>, Self::Error> {
Ok(None)
}
fn max_transfer_blocks(&self) -> u64 {
u64::from(u16::MAX)
}
}