Merge pull request #3843 from ost-ing/sdmmc-multiblocks
STM32 SDMMC multiple block read/write support
This commit is contained in:
commit
ca40dc7ff7
@ -151,6 +151,8 @@ pub enum Error {
|
|||||||
BadClock,
|
BadClock,
|
||||||
/// Signaling switch failed.
|
/// Signaling switch failed.
|
||||||
SignalingSwitchFailed,
|
SignalingSwitchFailed,
|
||||||
|
/// Underrun error
|
||||||
|
Underrun,
|
||||||
/// ST bit error.
|
/// ST bit error.
|
||||||
#[cfg(sdmmc_v1)]
|
#[cfg(sdmmc_v1)]
|
||||||
StBitErr,
|
StBitErr,
|
||||||
@ -1025,6 +1027,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
if status.dtimeout() {
|
if status.dtimeout() {
|
||||||
return Poll::Ready(Err(Error::Timeout));
|
return Poll::Ready(Err(Error::Timeout));
|
||||||
}
|
}
|
||||||
|
if status.txunderr() {
|
||||||
|
return Poll::Ready(Err(Error::Underrun));
|
||||||
|
}
|
||||||
#[cfg(sdmmc_v1)]
|
#[cfg(sdmmc_v1)]
|
||||||
if status.stbiterr() {
|
if status.stbiterr() {
|
||||||
return Poll::Ready(Err(Error::StBitErr));
|
return Poll::Ready(Err(Error::StBitErr));
|
||||||
@ -1080,6 +1085,73 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
res
|
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()?.get_capacity();
|
||||||
|
|
||||||
|
// 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::StandardCapacity => block_idx * 512,
|
||||||
|
_ => block_idx,
|
||||||
|
};
|
||||||
|
Self::cmd(common_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,
|
||||||
|
#[cfg(sdmmc_v1)]
|
||||||
|
&mut self.dma,
|
||||||
|
buffer,
|
||||||
|
512 * blocks.len() as u32,
|
||||||
|
9,
|
||||||
|
);
|
||||||
|
InterruptHandler::<T>::data_interrupts(true);
|
||||||
|
|
||||||
|
Self::cmd(common_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(common_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.
|
/// Write a data block.
|
||||||
pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {
|
pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {
|
||||||
let card = self.card.as_mut().ok_or(Error::NoCard)?;
|
let card = self.card.as_mut().ok_or(Error::NoCard)?;
|
||||||
@ -1088,7 +1160,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) };
|
let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) };
|
||||||
|
|
||||||
// Always read 1 block of 512 bytes
|
// Always read 1 block of 512 bytes
|
||||||
// SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
|
// cards are byte addressed hence the blockaddress is in multiples of 512 bytes
|
||||||
let address = match card.get_capacity() {
|
let address = match card.get_capacity() {
|
||||||
CardCapacity::StandardCapacity => block_idx * 512,
|
CardCapacity::StandardCapacity => block_idx * 512,
|
||||||
_ => block_idx,
|
_ => block_idx,
|
||||||
@ -1136,6 +1208,94 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.get_capacity() {
|
||||||
|
CardCapacity::StandardCapacity => block_idx * 512,
|
||||||
|
_ => block_idx,
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::cmd(common_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(common_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(common_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.txunderr() {
|
||||||
|
return Poll::Ready(Err(Error::Underrun));
|
||||||
|
}
|
||||||
|
#[cfg(sdmmc_v1)]
|
||||||
|
if status.stbiterr() {
|
||||||
|
return Poll::Ready(Err(Error::StBitErr));
|
||||||
|
}
|
||||||
|
if status.dataend() {
|
||||||
|
return Poll::Ready(Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Self::cmd(common_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
|
/// Get a reference to the initialized card
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@ -1699,33 +1859,35 @@ impl<'d, T: Instance> block_device_driver::BlockDevice<512> for Sdmmc<'d, T> {
|
|||||||
|
|
||||||
async fn read(
|
async fn read(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut block_address: u32,
|
block_address: u32,
|
||||||
buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>],
|
buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>],
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
// FIXME/TODO because of missing read_blocks multiple we have to do this one block at a time
|
// TODO: I think block_address needs to be adjusted by the partition start offset
|
||||||
for block in buf.iter_mut() {
|
if buf.len() == 1 {
|
||||||
// safety aligned by block device
|
let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) };
|
||||||
let block = unsafe { &mut *(block as *mut _ as *mut crate::sdmmc::DataBlock) };
|
|
||||||
self.read_block(block_address, block).await?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write(
|
async fn write(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut block_address: u32,
|
block_address: u32,
|
||||||
buf: &[aligned::Aligned<Self::Align, [u8; 512]>],
|
buf: &[aligned::Aligned<Self::Align, [u8; 512]>],
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
// FIXME/TODO because of missing read_blocks multiple we have to do this one block at a time
|
// TODO: I think block_address needs to be adjusted by the partition start offset
|
||||||
for block in buf.iter() {
|
if buf.len() == 1 {
|
||||||
// safety aligned by block device
|
let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) };
|
||||||
let block = unsafe { &*(block as *const _ as *const crate::sdmmc::DataBlock) };
|
|
||||||
self.write_block(block_address, block).await?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,8 +34,10 @@ async fn main(_spawner: Spawner) {
|
|||||||
pattern1[i] = i as u8;
|
pattern1[i] = i as u8;
|
||||||
pattern2[i] = !i as u8;
|
pattern2[i] = !i as u8;
|
||||||
}
|
}
|
||||||
|
let patterns = [pattern1.clone(), pattern2.clone()];
|
||||||
|
|
||||||
let mut block = DataBlock([0u8; 512]);
|
let mut block = DataBlock([0u8; 512]);
|
||||||
|
let mut blocks = [DataBlock([0u8; 512]), DataBlock([0u8; 512])];
|
||||||
|
|
||||||
// ======== Try 4bit. ==============
|
// ======== Try 4bit. ==============
|
||||||
info!("initializing in 4-bit mode...");
|
info!("initializing in 4-bit mode...");
|
||||||
@ -84,6 +86,13 @@ async fn main(_spawner: Spawner) {
|
|||||||
s.read_block(block_idx, &mut block).await.unwrap();
|
s.read_block(block_idx, &mut block).await.unwrap();
|
||||||
assert_eq!(block, pattern2);
|
assert_eq!(block, pattern2);
|
||||||
|
|
||||||
|
info!("writing blocks [pattern1, pattern2]...");
|
||||||
|
s.write_blocks(block_idx, &patterns).await.unwrap();
|
||||||
|
|
||||||
|
info!("reading blocks...");
|
||||||
|
s.read_blocks(block_idx, &mut blocks).await.unwrap();
|
||||||
|
assert_eq!(&blocks, &patterns);
|
||||||
|
|
||||||
drop(s);
|
drop(s);
|
||||||
|
|
||||||
// ======== Try 1bit. ==============
|
// ======== Try 1bit. ==============
|
||||||
@ -116,9 +125,9 @@ async fn main(_spawner: Spawner) {
|
|||||||
info!("Card: {:#?}", Debug2Format(card));
|
info!("Card: {:#?}", Debug2Format(card));
|
||||||
info!("Clock: {}", s.clock());
|
info!("Clock: {}", s.clock());
|
||||||
|
|
||||||
info!("reading pattern2 written in 4bit mode...");
|
info!("reading pattern1 written in 4bit mode...");
|
||||||
s.read_block(block_idx, &mut block).await.unwrap();
|
s.read_block(block_idx, &mut block).await.unwrap();
|
||||||
assert_eq!(block, pattern2);
|
assert_eq!(block, pattern1);
|
||||||
|
|
||||||
info!("writing pattern1...");
|
info!("writing pattern1...");
|
||||||
s.write_block(block_idx, &pattern1).await.unwrap();
|
s.write_block(block_idx, &pattern1).await.unwrap();
|
||||||
@ -134,6 +143,13 @@ async fn main(_spawner: Spawner) {
|
|||||||
s.read_block(block_idx, &mut block).await.unwrap();
|
s.read_block(block_idx, &mut block).await.unwrap();
|
||||||
assert_eq!(block, pattern2);
|
assert_eq!(block, pattern2);
|
||||||
|
|
||||||
|
info!("writing blocks [pattern1, pattern2]...");
|
||||||
|
s.write_blocks(block_idx, &patterns).await.unwrap();
|
||||||
|
|
||||||
|
info!("reading blocks...");
|
||||||
|
s.read_blocks(block_idx, &mut blocks).await.unwrap();
|
||||||
|
assert_eq!(&blocks, &patterns);
|
||||||
|
|
||||||
drop(s);
|
drop(s);
|
||||||
|
|
||||||
info!("Test OK");
|
info!("Test OK");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user