Skip to content

Commit 4ec85c9

Browse files
committed
Enforce correct SD state at compilation using a new type
The struct `SdMmcSpi` had two separate methods for initialization and deinitialization. It was up to the user not to mess them up at runtime. A new `BlockSpi` struct takes over `BlockDevice` interface duties, making it impossible to use block procedures while the SD interface is in the wrong state.
1 parent 17d24ba commit 4ec85c9

File tree

2 files changed

+153
-141
lines changed

2 files changed

+153
-141
lines changed

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pub use crate::filesystem::{
8888
Timestamp, MAX_FILE_SIZE,
8989
};
9090
pub use crate::sdmmc::Error as SdMmcError;
91-
pub use crate::sdmmc::SdMmcSpi;
91+
pub use crate::sdmmc::{BlockSpi, SdMmcSpi};
9292

9393
// ****************************************************************************
9494
//

src/sdmmc.rs

+152-140
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use core::cell::RefCell;
1111

1212
const DEFAULT_DELAY_COUNT: u32 = 32_000;
1313

14-
/// Represents an SD Card interface built from an SPI peripheral and a Chip
14+
/// Represents an inactive SD Card interface.
15+
/// Built from an SPI peripheral and a Chip
1516
/// Select pin. We need Chip Select to be separate so we can clock out some
1617
/// bytes without Chip Select asserted (which puts the card into SPI mode).
1718
pub struct SdMmcSpi<SPI, CS>
@@ -26,6 +27,14 @@ where
2627
state: State,
2728
}
2829

30+
/// An initialized block device used to access the SD card.
31+
/// Uses SPI mode.
32+
pub struct BlockSpi<'a, SPI, CS>(&'a mut SdMmcSpi<SPI, CS>)
33+
where
34+
SPI: embedded_hal::blocking::spi::Transfer<u8>,
35+
CS: embedded_hal::digital::v2::OutputPin,
36+
<SPI as embedded_hal::blocking::spi::Transfer<u8>>::Error: core::fmt::Debug;
37+
2938
/// The possible errors `SdMmcSpi` can generate.
3039
#[derive(Debug, Copy, Clone)]
3140
pub enum Error {
@@ -132,12 +141,6 @@ where
132141
}
133142
}
134143

135-
/// Get a temporary borrow on the underlying SPI device. Useful if you
136-
/// need to re-clock the SPI after performing `init()`.
137-
pub fn spi(&mut self) -> core::cell::RefMut<SPI> {
138-
self.spi.borrow_mut()
139-
}
140-
141144
fn cs_high(&self) -> Result<(), Error> {
142145
self.cs
143146
.borrow_mut()
@@ -149,19 +152,13 @@ where
149152
self.cs.borrow_mut().set_low().map_err(|_| Error::GpioError)
150153
}
151154

152-
/// This routine must be performed with an SPI clock speed of around 100 - 400 kHz.
153-
/// Afterwards you may increase the SPI clock speed.
154-
pub fn init(&mut self) -> Result<(), Error> {
155-
self.init_with_opts(Default::default())
156-
}
157-
158-
/// De-init the card so it can't be used
159-
pub fn deinit(&mut self) {
160-
self.state = State::NoInit;
155+
/// Initializes the card into a known state
156+
pub fn acquire(&mut self) -> Result<BlockSpi<SPI, CS>, Error> {
157+
self.acquire_with_opts(Default::default())
161158
}
162159

163160
/// Initializes the card into a known state
164-
pub fn init_with_opts(&mut self, options: AcquireOpts) -> Result<(), Error> {
161+
pub fn acquire_with_opts(&mut self, options: AcquireOpts) -> Result<BlockSpi<SPI, CS>, Error> {
165162
let f = |s: &mut Self| {
166163
// Assume it hasn't worked
167164
s.state = State::Error;
@@ -244,47 +241,7 @@ where
244241
let result = f(self);
245242
self.cs_high()?;
246243
let _ = self.receive();
247-
result
248-
}
249-
250-
/// Return the usable size of this SD card in bytes.
251-
pub fn card_size_bytes(&self) -> Result<u64, Error> {
252-
self.check_state()?;
253-
self.with_chip_select(|s| {
254-
let csd = s.read_csd()?;
255-
match csd {
256-
Csd::V1(ref contents) => Ok(contents.card_capacity_bytes()),
257-
Csd::V2(ref contents) => Ok(contents.card_capacity_bytes()),
258-
}
259-
})
260-
}
261-
262-
/// Erase some blocks on the card.
263-
pub fn erase(&mut self, _first_block: BlockIdx, _last_block: BlockIdx) -> Result<(), Error> {
264-
self.check_state()?;
265-
unimplemented!();
266-
}
267-
268-
/// Can this card erase single blocks?
269-
pub fn erase_single_block_enabled(&self) -> Result<bool, Error> {
270-
self.check_state()?;
271-
self.with_chip_select(|s| {
272-
let csd = s.read_csd()?;
273-
match csd {
274-
Csd::V1(ref contents) => Ok(contents.erase_single_block_enabled()),
275-
Csd::V2(ref contents) => Ok(contents.erase_single_block_enabled()),
276-
}
277-
})
278-
}
279-
280-
/// Return an error if we're not in `State::Idle`. It probably means
281-
/// they haven't called `begin()`.
282-
fn check_state(&self) -> Result<(), Error> {
283-
if self.state != State::Idle {
284-
Err(Error::BadState)
285-
} else {
286-
Ok(())
287-
}
244+
result.map(move |()| BlockSpi(self))
288245
}
289246

290247
/// Perform a function that might error with the chipselect low.
@@ -311,77 +268,6 @@ where
311268
result
312269
}
313270

314-
/// Read the 'card specific data' block.
315-
fn read_csd(&self) -> Result<Csd, Error> {
316-
match self.card_type {
317-
CardType::SD1 => {
318-
let mut csd = CsdV1::new();
319-
if self.card_command(CMD9, 0)? != 0 {
320-
return Err(Error::RegisterReadError);
321-
}
322-
self.read_data(&mut csd.data)?;
323-
Ok(Csd::V1(csd))
324-
}
325-
CardType::SD2 | CardType::SDHC => {
326-
let mut csd = CsdV2::new();
327-
if self.card_command(CMD9, 0)? != 0 {
328-
return Err(Error::RegisterReadError);
329-
}
330-
self.read_data(&mut csd.data)?;
331-
Ok(Csd::V2(csd))
332-
}
333-
}
334-
}
335-
336-
/// Read an arbitrary number of bytes from the card. Always fills the
337-
/// given buffer, so make sure it's the right size.
338-
fn read_data(&self, buffer: &mut [u8]) -> Result<(), Error> {
339-
// Get first non-FF byte.
340-
let mut delay = Delay::new();
341-
let status = loop {
342-
let s = self.receive()?;
343-
if s != 0xFF {
344-
break s;
345-
}
346-
delay.delay(Error::TimeoutReadBuffer)?;
347-
};
348-
if status != DATA_START_BLOCK {
349-
return Err(Error::ReadError);
350-
}
351-
352-
for b in buffer.iter_mut() {
353-
*b = self.receive()?;
354-
}
355-
356-
let mut crc = u16::from(self.receive()?);
357-
crc <<= 8;
358-
crc |= u16::from(self.receive()?);
359-
360-
let calc_crc = crc16(buffer);
361-
if crc != calc_crc {
362-
return Err(Error::CrcError(crc, calc_crc));
363-
}
364-
365-
Ok(())
366-
}
367-
368-
/// Write an arbitrary number of bytes to the card.
369-
fn write_data(&self, token: u8, buffer: &[u8]) -> Result<(), Error> {
370-
let calc_crc = crc16(buffer);
371-
self.send(token)?;
372-
for &b in buffer.iter() {
373-
self.send(b)?;
374-
}
375-
self.send((calc_crc >> 8) as u8)?;
376-
self.send(calc_crc as u8)?;
377-
let status = self.receive()?;
378-
if (status & DATA_RES_MASK) != DATA_RES_ACCEPTED {
379-
Err(Error::WriteError)
380-
} else {
381-
Ok(())
382-
}
383-
}
384-
385271
/// Perform an application-specific command.
386272
fn card_acmd(&self, command: u8, arg: u32) -> Result<u8, Error> {
387273
self.card_command(CMD55, 0)?;
@@ -454,7 +340,124 @@ where
454340
}
455341
}
456342

457-
impl<SPI, CS> BlockDevice for SdMmcSpi<SPI, CS>
343+
impl<SPI, CS> BlockSpi<'_, SPI, CS>
344+
where
345+
SPI: embedded_hal::blocking::spi::Transfer<u8>,
346+
CS: embedded_hal::digital::v2::OutputPin,
347+
<SPI as embedded_hal::blocking::spi::Transfer<u8>>::Error: core::fmt::Debug,
348+
{
349+
/// Get a temporary borrow on the underlying SPI device. Useful if you
350+
/// need to re-clock the SPI.
351+
pub fn spi(&mut self) -> core::cell::RefMut<SPI> {
352+
self.0.spi.borrow_mut()
353+
}
354+
355+
/// Mark the card as unused.
356+
// If there is any need to flush data, it should be implemented here.
357+
fn deinit(&mut self) {
358+
self.0.state = State::NoInit;
359+
}
360+
361+
/// Return the usable size of this SD card in bytes.
362+
pub fn card_size_bytes(&self) -> Result<u64, Error> {
363+
self.0.with_chip_select(|_s| {
364+
let csd = self.read_csd()?;
365+
match csd {
366+
Csd::V1(ref contents) => Ok(contents.card_capacity_bytes()),
367+
Csd::V2(ref contents) => Ok(contents.card_capacity_bytes()),
368+
}
369+
})
370+
}
371+
372+
/// Erase some blocks on the card.
373+
pub fn erase(&mut self, _first_block: BlockIdx, _last_block: BlockIdx) -> Result<(), Error> {
374+
unimplemented!();
375+
}
376+
377+
/// Can this card erase single blocks?
378+
pub fn erase_single_block_enabled(&self) -> Result<bool, Error> {
379+
self.0.with_chip_select(|_s| {
380+
let csd = self.read_csd()?;
381+
match csd {
382+
Csd::V1(ref contents) => Ok(contents.erase_single_block_enabled()),
383+
Csd::V2(ref contents) => Ok(contents.erase_single_block_enabled()),
384+
}
385+
})
386+
}
387+
388+
/// Read the 'card specific data' block.
389+
fn read_csd(&self) -> Result<Csd, Error> {
390+
match self.0.card_type {
391+
CardType::SD1 => {
392+
let mut csd = CsdV1::new();
393+
if self.0.card_command(CMD9, 0)? != 0 {
394+
return Err(Error::RegisterReadError);
395+
}
396+
self.read_data(&mut csd.data)?;
397+
Ok(Csd::V1(csd))
398+
}
399+
CardType::SD2 | CardType::SDHC => {
400+
let mut csd = CsdV2::new();
401+
if self.0.card_command(CMD9, 0)? != 0 {
402+
return Err(Error::RegisterReadError);
403+
}
404+
self.read_data(&mut csd.data)?;
405+
Ok(Csd::V2(csd))
406+
}
407+
}
408+
}
409+
410+
/// Read an arbitrary number of bytes from the card. Always fills the
411+
/// given buffer, so make sure it's the right size.
412+
fn read_data(&self, buffer: &mut [u8]) -> Result<(), Error> {
413+
// Get first non-FF byte.
414+
let mut delay = Delay::new();
415+
let status = loop {
416+
let s = self.0.receive()?;
417+
if s != 0xFF {
418+
break s;
419+
}
420+
delay.delay(Error::TimeoutReadBuffer)?;
421+
};
422+
if status != DATA_START_BLOCK {
423+
return Err(Error::ReadError);
424+
}
425+
426+
for b in buffer.iter_mut() {
427+
*b = self.0.receive()?;
428+
}
429+
430+
let mut crc = u16::from(self.0.receive()?);
431+
crc <<= 8;
432+
crc |= u16::from(self.0.receive()?);
433+
434+
let calc_crc = crc16(buffer);
435+
if crc != calc_crc {
436+
return Err(Error::CrcError(crc, calc_crc));
437+
}
438+
439+
Ok(())
440+
}
441+
442+
/// Write an arbitrary number of bytes to the card.
443+
fn write_data(&self, token: u8, buffer: &[u8]) -> Result<(), Error> {
444+
let calc_crc = crc16(buffer);
445+
self.0.send(token)?;
446+
for &b in buffer.iter() {
447+
self.0.send(b)?;
448+
}
449+
self.0.send((calc_crc >> 8) as u8)?;
450+
self.0.send(calc_crc as u8)?;
451+
let status = self.0.receive()?;
452+
if (status & DATA_RES_MASK) != DATA_RES_ACCEPTED {
453+
Err(Error::WriteError)
454+
} else {
455+
Ok(())
456+
}
457+
}
458+
}
459+
460+
impl<SPI, CS> BlockDevice for BlockSpi<'_, SPI, CS>
458461
where
459462
SPI: embedded_hal::blocking::spi::Transfer<u8>,
460463
<SPI as embedded_hal::blocking::spi::Transfer<u8>>::Error: core::fmt::Debug,
@@ -469,21 +472,20 @@ where
469472
start_block_idx: BlockIdx,
470473
_reason: &str,
471474
) -> Result<(), Self::Error> {
472-
self.check_state()?;
473-
let start_idx = match self.card_type {
475+
let start_idx = match self.0.card_type {
474476
CardType::SD1 | CardType::SD2 => start_block_idx.0 * 512,
475477
CardType::SDHC => start_block_idx.0,
476478
};
477-
self.with_chip_select(|s| {
479+
self.0.with_chip_select(|s| {
478480
if blocks.len() == 1 {
479481
// Start a single-block read
480482
s.card_command(CMD17, start_idx)?;
481-
s.read_data(&mut blocks[0].contents)?;
483+
self.read_data(&mut blocks[0].contents)?;
482484
} else {
483485
// Start a multi-block read
484486
s.card_command(CMD18, start_idx)?;
485487
for block in blocks.iter_mut() {
486-
s.read_data(&mut block.contents)?;
488+
self.read_data(&mut block.contents)?;
487489
}
488490
// Stop the read
489491
s.card_command(CMD12, 0)?;
@@ -494,16 +496,15 @@ where
494496

495497
/// Write one or more blocks, starting at the given block index.
496498
fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
497-
self.check_state()?;
498-
let start_idx = match self.card_type {
499+
let start_idx = match self.0.card_type {
499500
CardType::SD1 | CardType::SD2 => start_block_idx.0 * 512,
500501
CardType::SDHC => start_block_idx.0,
501502
};
502-
self.with_chip_select_mut(|s| {
503+
self.0.with_chip_select_mut(|s| {
503504
if blocks.len() == 1 {
504505
// Start a single-block write
505506
s.card_command(CMD24, start_idx)?;
506-
s.write_data(DATA_START_BLOCK, &blocks[0].contents)?;
507+
self.write_data(DATA_START_BLOCK, &blocks[0].contents)?;
507508
s.wait_not_busy()?;
508509
if s.card_command(CMD13, 0)? != 0x00 {
509510
return Err(Error::WriteError);
@@ -516,7 +517,7 @@ where
516517
s.card_command(CMD25, start_idx)?;
517518
for block in blocks.iter() {
518519
s.wait_not_busy()?;
519-
s.write_data(WRITE_MULTIPLE_TOKEN, &block.contents)?;
520+
self.write_data(WRITE_MULTIPLE_TOKEN, &block.contents)?;
520521
}
521522
// Stop the write
522523
s.wait_not_busy()?;
@@ -534,6 +535,17 @@ where
534535
}
535536
}
536537

538+
impl<SPI, CS> Drop for BlockSpi<'_, SPI, CS>
539+
where
540+
SPI: embedded_hal::blocking::spi::Transfer<u8>,
541+
<SPI as embedded_hal::blocking::spi::Transfer<u8>>::Error: core::fmt::Debug,
542+
CS: embedded_hal::digital::v2::OutputPin,
543+
{
544+
fn drop(&mut self) {
545+
self.deinit()
546+
}
547+
}
548+
537549
// ****************************************************************************
538550
//
539551
// End Of File

0 commit comments

Comments
 (0)