Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STM32 SDMMC multiple block read/write support #3843

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 171 additions & 20 deletions embassy-stm32/src/sdmmc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,66 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
res
}

/// Read multiple data blocks.
#[inline]
pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> {
let card_capacity = self.card()?.card_type;

// NOTE(unsafe) reinterpret buffer as &mut [u32]
let buffer = unsafe {
let ptr = blocks.as_mut_ptr() as *mut u32;
let len = blocks.len() * 128;
core::slice::from_raw_parts_mut(ptr, len)
};

// Always read 1 block of 512 bytes
// SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
let address = match card_capacity {
CardCapacity::SDSC => block_idx * 512,
_ => block_idx,
};
Self::cmd(Cmd::set_block_length(512), false)?; // CMD16

let regs = T::regs();
let on_drop = OnDrop::new(|| Self::on_drop());

let transfer = Self::prepare_datapath_read(&self.config, &mut self.dma, buffer, 512 * blocks.len() as u32, 9);
InterruptHandler::<T>::data_interrupts(true);

Self::cmd(Cmd::read_multiple_blocks(address), true)?;

let res = poll_fn(|cx| {
T::state().register(cx.waker());
let status = regs.star().read();

if status.dcrcfail() {
return Poll::Ready(Err(Error::Crc));
}
if status.dtimeout() {
return Poll::Ready(Err(Error::Timeout));
}
#[cfg(sdmmc_v1)]
if status.stbiterr() {
return Poll::Ready(Err(Error::StBitErr));
}
if status.dataend() {
return Poll::Ready(Ok(()));
}
Poll::Pending
})
.await;

Self::cmd(Cmd::stop_transmission(), false)?; // CMD12
Self::clear_interrupt_flags();

if res.is_ok() {
on_drop.defuse();
Self::stop_datapath();
drop(transfer);
}
res
}

/// Write a data block.
pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {
let card = self.card.as_mut().ok_or(Error::NoCard)?;
Expand Down Expand Up @@ -1322,6 +1382,90 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
}
}

/// Write multiple data blocks.
pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> {
let card = self.card.as_mut().ok_or(Error::NoCard)?;

// NOTE(unsafe) reinterpret buffer as &[u32]
let buffer = unsafe {
let ptr = blocks.as_ptr() as *const u32;
let len = blocks.len() * 128;
core::slice::from_raw_parts(ptr, len)
};

// Always read 1 block of 512 bytes
// SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
let address = match card.card_type {
CardCapacity::SDSC => block_idx * 512,
_ => block_idx,
};

Self::cmd(Cmd::set_block_length(512), false)?; // CMD16

let block_count = blocks.len();

let regs = T::regs();
let on_drop = OnDrop::new(|| Self::on_drop());

#[cfg(sdmmc_v1)]
Self::cmd(Cmd::write_multiple_blocks(address), true)?; // CMD25

// Setup write command
let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9);
InterruptHandler::<T>::data_interrupts(true);

#[cfg(sdmmc_v2)]
Self::cmd(Cmd::write_multiple_blocks(address), true)?; // CMD25

let res = poll_fn(|cx| {
T::state().register(cx.waker());
let status = regs.star().read();

if status.dcrcfail() {
return Poll::Ready(Err(Error::Crc));
}
if status.dtimeout() {
return Poll::Ready(Err(Error::Timeout));
}
// if status.d
#[cfg(sdmmc_v1)]
if status.stbiterr() {
return Poll::Ready(Err(Error::StBitErr));
}
if status.dataend() {
return Poll::Ready(Ok(()));
}
Poll::Pending
})
.await;

Self::cmd(Cmd::stop_transmission(), false)?; // CMD12
Self::clear_interrupt_flags();

match res {
Ok(_) => {
on_drop.defuse();
Self::stop_datapath();
drop(transfer);

// TODO: Make this configurable
let mut timeout: u32 = 0x00FF_FFFF;

// Try to read card status (ACMD13)
while timeout > 0 {
match self.read_sd_status().await {
Ok(_) => return Ok(()),
Err(Error::Timeout) => (), // Try again
Err(e) => return Err(e),
}
timeout -= 1;
}
Err(Error::SoftwareTimeout)
}
Err(e) => Err(e),
}
}

/// Get a reference to the initialized card
///
/// # Errors
Expand Down Expand Up @@ -1412,9 +1556,9 @@ impl Cmd {
}

/// CMD12:
//const fn stop_transmission() -> Cmd {
// Cmd::new(12, 0, Response::Short)
//}
const fn stop_transmission() -> Cmd {
Cmd::new(12, 0, Response::Short)
}

/// CMD13: Ask card to send status register
/// ACMD13: SD Status
Expand All @@ -1433,15 +1577,20 @@ impl Cmd {
}

/// CMD18: Multiple Block Read
//const fn read_multiple_blocks(addr: u32) -> Cmd {
// Cmd::new(18, addr, Response::Short)
//}
const fn read_multiple_blocks(addr: u32) -> Cmd {
Cmd::new(18, addr, Response::Short)
}

/// CMD24: Block Write
const fn write_single_block(addr: u32) -> Cmd {
Cmd::new(24, addr, Response::Short)
}

/// CMD25: Multiple Block Write
const fn write_multiple_blocks(addr: u32) -> Cmd {
Cmd::new(25, addr, Response::Short)
}

const fn app_op_cmd(arg: u32) -> Cmd {
Cmd::new(41, arg, Response::Short)
}
Expand Down Expand Up @@ -1518,33 +1667,35 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> block_device_driver::BlockDevice<51

async fn read(
&mut self,
mut block_address: u32,
block_address: u32,
buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>],
) -> Result<(), Self::Error> {
// FIXME/TODO because of missing read_blocks multiple we have to do this one block at a time
for block in buf.iter_mut() {
// safety aligned by block device
let block = unsafe { &mut *(block as *mut _ as *mut crate::sdmmc::DataBlock) };
// TODO: I think block_address needs to be adjusted by the partition start offset
if buf.len() == 1 {
let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) };
self.read_block(block_address, block).await?;
block_address += 1;
} else {
let blocks: &mut [DataBlock] =
unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) };
self.read_blocks(block_address, blocks).await?;
}

Ok(())
}

async fn write(
&mut self,
mut block_address: u32,
block_address: u32,
buf: &[aligned::Aligned<Self::Align, [u8; 512]>],
) -> Result<(), Self::Error> {
// FIXME/TODO because of missing read_blocks multiple we have to do this one block at a time
for block in buf.iter() {
// safety aligned by block device
let block = unsafe { &*(block as *const _ as *const crate::sdmmc::DataBlock) };
// TODO: I think block_address needs to be adjusted by the partition start offset
if buf.len() == 1 {
let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) };
self.write_block(block_address, block).await?;
block_address += 1;
} else {
let blocks: &[DataBlock] =
unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) };
self.write_blocks(block_address, blocks).await?;
}

Ok(())
}

Expand Down
15 changes: 15 additions & 0 deletions tests/stm32/src/bin/sdmmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ async fn main(_spawner: Spawner) {
}

let mut block = DataBlock([0u8; 512]);
let mut blocks = [DataBlock([0u8; 512]), DataBlock([0u8; 512])];

// ======== Try 4bit. ==============
info!("initializing in 4-bit mode...");
Expand Down Expand Up @@ -84,6 +85,13 @@ async fn main(_spawner: Spawner) {
s.read_block(block_idx, &mut block).await.unwrap();
assert_eq!(block, pattern2);

info!("writing blocks [pattern1, pattern2]...");
s.write_blocks(block_idx, &[pattern1, pattern2]).await.unwrap();

info!("reading blocks...");
s.read_blocks(block_idx, &mut blocks).await.unwrap();
assert_eq!(blocks, [pattern1, pattern2]);

drop(s);

// ======== Try 1bit. ==============
Expand Down Expand Up @@ -134,6 +142,13 @@ async fn main(_spawner: Spawner) {
s.read_block(block_idx, &mut block).await.unwrap();
assert_eq!(block, pattern2);

info!("writing blocks [pattern1, pattern2]...");
s.write_blocks(block_idx, &[pattern1, pattern2]).await.unwrap();

info!("reading blocks...");
s.read_blocks(block_idx, &mut blocks).await.unwrap();
assert_eq!(blocks, [pattern1, pattern2]);

drop(s);

info!("Test OK");
Expand Down