Skip to content

Commit f8e3ec8

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 0fbfaf4 commit f8e3ec8

File tree

2 files changed

+153
-140
lines changed

2 files changed

+153
-140
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-139
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 {
@@ -134,12 +143,6 @@ where
134143
}
135144
}
136145

137-
/// Get a temporary borrow on the underlying SPI device. Useful if you
138-
/// need to re-clock the SPI after performing `init()`.
139-
pub fn spi(&mut self) -> core::cell::RefMut<SPI> {
140-
self.spi.borrow_mut()
141-
}
142-
143146
fn cs_high(&self) -> Result<(), Error> {
144147
self.cs
145148
.borrow_mut()
@@ -151,19 +154,13 @@ where
151154
self.cs.borrow_mut().set_low().map_err(|_| Error::GpioError)
152155
}
153156

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

165162
/// Initializes the card into a known state
166-
pub fn init_with_opts(&mut self, options: AcquireOpts) -> Result<(), Error> {
163+
pub fn acquire_with_opts(&mut self, options: AcquireOpts) -> Result<BlockSpi<SPI, CS>, Error> {
167164
let f = |s: &mut Self| {
168165
// Assume it hasn't worked
169166
s.state = State::Error;
@@ -248,48 +245,9 @@ where
248245
let result = f(self);
249246
self.cs_high()?;
250247
let _ = self.receive();
251-
result
252-
}
253-
254-
/// Return the usable size of this SD card in bytes.
255-
pub fn card_size_bytes(&self) -> Result<u64, Error> {
256-
self.check_state()?;
257-
self.with_chip_select(|s| {
258-
let csd = s.read_csd()?;
259-
match csd {
260-
Csd::V1(ref contents) => Ok(contents.card_capacity_bytes()),
261-
Csd::V2(ref contents) => Ok(contents.card_capacity_bytes()),
262-
}
263-
})
264-
}
265-
266-
/// Erase some blocks on the card.
267-
pub fn erase(&mut self, _first_block: BlockIdx, _last_block: BlockIdx) -> Result<(), Error> {
268-
self.check_state()?;
269-
unimplemented!();
248+
result.map(move |()| BlockSpi(self))
270249
}
271250

272-
/// Can this card erase single blocks?
273-
pub fn erase_single_block_enabled(&self) -> Result<bool, Error> {
274-
self.check_state()?;
275-
self.with_chip_select(|s| {
276-
let csd = s.read_csd()?;
277-
match csd {
278-
Csd::V1(ref contents) => Ok(contents.erase_single_block_enabled()),
279-
Csd::V2(ref contents) => Ok(contents.erase_single_block_enabled()),
280-
}
281-
})
282-
}
283-
284-
/// Return an error if we're not in `State::Idle`. It probably means
285-
/// they haven't called `begin()`.
286-
fn check_state(&self) -> Result<(), Error> {
287-
if self.state != State::Idle {
288-
Err(Error::BadState)
289-
} else {
290-
Ok(())
291-
}
292-
}
293251

294252
/// Perform a function that might error with the chipselect low.
295253
/// Always releases the chipselect, even if the function errors.
@@ -315,77 +273,6 @@ where
315273
result
316274
}
317275

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

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

499502
/// Write one or more blocks, starting at the given block index.
500503
fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> {
501-
self.check_state()?;
502-
let start_idx = match self.card_type {
504+
let start_idx = match self.0.card_type {
503505
CardType::SD1 | CardType::SD2 => start_block_idx.0 * 512,
504506
CardType::SDHC => start_block_idx.0,
505507
};
506-
self.with_chip_select_mut(|s| {
508+
self.0.with_chip_select_mut(|s| {
507509
if blocks.len() == 1 {
508510
// Start a single-block write
509511
s.card_command(CMD24, start_idx)?;
510-
s.write_data(DATA_START_BLOCK, &blocks[0].contents)?;
512+
self.write_data(DATA_START_BLOCK, &blocks[0].contents)?;
511513
s.wait_not_busy()?;
512514
if s.card_command(CMD13, 0)? != 0x00 {
513515
return Err(Error::WriteError);
@@ -520,7 +522,7 @@ where
520522
s.card_command(CMD25, start_idx)?;
521523
for block in blocks.iter() {
522524
s.wait_not_busy()?;
523-
s.write_data(WRITE_MULTIPLE_TOKEN, &block.contents)?;
525+
self.write_data(WRITE_MULTIPLE_TOKEN, &block.contents)?;
524526
}
525527
// Stop the write
526528
s.wait_not_busy()?;
@@ -538,6 +540,17 @@ where
538540
}
539541
}
540542

543+
impl<SPI, CS> Drop for BlockSpi<'_, SPI, CS>
544+
where
545+
SPI: embedded_hal::blocking::spi::Transfer<u8>,
546+
<SPI as embedded_hal::blocking::spi::Transfer<u8>>::Error: core::fmt::Debug,
547+
CS: embedded_hal::digital::v2::OutputPin,
548+
{
549+
fn drop(&mut self) {
550+
self.deinit()
551+
}
552+
}
553+
541554
// ****************************************************************************
542555
//
543556
// End Of File

0 commit comments

Comments
 (0)