sdmmc_spi/
lib.rs

1//! SD/MMC Library written in Embedded Rust, that inspired by [embedded-sdmmc](https://crates.io/crates/embedded-sdmmc).
2//!
3//! This crate is intended to allow you to init/read/write SD/MMC devices by SPI bus.
4
5#![no_std]
6
7mod config;
8mod consts;
9mod crc;
10mod csd;
11mod response;
12
13pub use crate::config::{DefaultSdMmcSpiConfig, SdMmcSpiConfig};
14pub use diskio::{
15    BlockSize, DiskioDevice, Error as DiskioError, IoctlCmd, Lba, Status, StatusFlag,
16};
17
18use crate::{
19    consts::{commands, tokens, BLOCK_SIZE},
20    crc::{crc16, crc7},
21    csd::{CapacityProvider, Csd, CsdData, CsdV1, CsdV2},
22    response::R1Response,
23};
24
25use core::{cell::RefCell, marker::PhantomData};
26use defmt::{error, info, warn, Format};
27use embedded_hal::blocking::spi::Transfer;
28use switch_hal::OutputSwitch;
29
30/// [`SdMmcSpi`] result error.
31///
32/// `T` - transport error type.
33/// `S` - select switch type.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum Error<T, S> {
36    /// Error from the SPI peripheral.
37    Transport(T),
38    /// Couldn't set a select.
39    SelectError(S),
40    /// Failed to enable CRC checking on the card.
41    CantEnableCRC,
42    /// No response when reading data from the card.
43    TimeoutReadBuffer,
44    /// No response when waiting for the card to not be busy.
45    TimeoutWaitAvailable,
46    /// No response when executing this command.
47    TimeoutCommand(u8),
48    /// Command error.
49    ErrorCommand(u8),
50    /// Failed to read the Card Specific Data register.
51    RegisterReadError,
52    /// CRC mismatch (card, host).
53    CrcError(u16, u16),
54    /// Error reading from the card.
55    ReadError,
56    /// Error writing to the card.
57    WriteError,
58    /// Can't perform this operation with the card in this state.
59    BadState,
60    /// Couldn't find the card.
61    CardNotFound,
62}
63
64/// Card type.
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Format)]
66pub enum CardType {
67    SD1,
68    SD2,
69    SDHC,
70}
71
72/// Error type alias.
73type ErrorFor<T> = <T as DiskioDevice>::HardwareError;
74
75/// SD Card SPI driver.
76///
77/// `Spi` - SPI.
78/// `Cs` - Chip select output switch.
79/// `Config` - Config implementation of driver config trait.
80pub struct SdMmcSpi<Spi: Transfer<u8>, Cs: OutputSwitch, Config: SdMmcSpiConfig> {
81    spi: RefCell<Spi>,
82    cs: RefCell<Cs>,
83    status: Status,
84    card_type: CardType,
85    csd: Csd,
86    config: PhantomData<Config>,
87}
88
89impl<Spi: Transfer<u8>, Cs: OutputSwitch, Config: SdMmcSpiConfig> SdMmcSpi<Spi, Cs, Config>
90where
91    Spi::Error: core::fmt::Debug,
92    Cs::Error: core::fmt::Debug,
93{
94    /// Init sequence value.
95    const INIT_SET_VALUE: u8 = 0xFF;
96    /// Init sequence size.
97    const INIT_SET_SIZE: usize = 10;
98    /// Receive transfer token.
99    const RECEIVE_TRANSFER_TOKEN: u8 = 0xFF;
100
101    /// Creates a new [`SdMmcSpi<Spi, Cs, Config>`].
102    ///
103    /// `spi` - SPI instance.
104    /// `cs` - chip select output switch.
105    pub fn new(spi: Spi, cs: Cs) -> Self {
106        SdMmcSpi {
107            spi: RefCell::new(spi),
108            cs: RefCell::new(cs),
109            status: StatusFlag::NotInitialized.into(),
110            card_type: CardType::SD1,
111            csd: Csd::V1(CsdV1(0)),
112            config: PhantomData::<Config>,
113        }
114    }
115
116    /// Validate buffer for read/write.
117    fn validate_buffer_len(buf_len: usize) -> Result<(), DiskioError<ErrorFor<Self>>> {
118        if buf_len == 0 || buf_len % BLOCK_SIZE != 0 {
119            error!(
120                "SD invalid buffer, length: {}, block size: {}",
121                buf_len, BLOCK_SIZE
122            );
123            Err(DiskioError::InvalidArgument)
124        } else {
125            Ok(())
126        }
127    }
128
129    /// Validate initialzed.
130    fn validate_initialized(&self) -> Result<(), DiskioError<ErrorFor<Self>>> {
131        if self.status.contains(StatusFlag::NotInitialized) {
132            Err(DiskioError::NotInitialized)
133        } else {
134            Ok(())
135        }
136    }
137
138    /// Get count of blocks in buffer.
139    fn get_block_count(buf_len: usize) -> usize {
140        buf_len / BLOCK_SIZE
141    }
142
143    /// Delay.
144    fn delay() {
145        for i in 0..Config::DELAY_DUMMY_CYCLES {
146            unsafe { core::ptr::read_volatile(&i) };
147        }
148    }
149
150    /// Convert lba.
151    fn convert_lba(&self, lba: Lba) -> u32 {
152        match self.card_type {
153            CardType::SD1 | CardType::SD2 => (lba as usize * BLOCK_SIZE) as u32,
154            CardType::SDHC => lba as u32,
155        }
156    }
157
158    /// Activate chip select.
159    fn select(&self) -> Result<(), ErrorFor<Self>> {
160        self.cs.borrow_mut().on().map_err(Error::SelectError)
161    }
162
163    /// Deactivate chip select.
164    fn unselect(&self) -> Result<(), ErrorFor<Self>> {
165        self.cs.borrow_mut().off().map_err(Error::SelectError)
166    }
167
168    /// CS scope.
169    fn cs_scope<F>(&self, f: F) -> Result<(), ErrorFor<Self>>
170    where
171        F: FnOnce(&Self) -> Result<(), ErrorFor<Self>>,
172    {
173        self.select()?;
174        let result = f(self);
175        self.unselect()?;
176
177        result
178    }
179
180    /// CS scope mut.
181    fn cs_scope_mut<F>(&mut self, f: F) -> Result<(), ErrorFor<Self>>
182    where
183        F: FnOnce(&mut Self) -> Result<(), ErrorFor<Self>>,
184    {
185        self.select()?;
186        let result = f(self);
187        self.unselect()?;
188
189        result
190    }
191
192    /// Send one byte and receive one byte.
193    fn transfer(&self, data: u8) -> Result<u8, ErrorFor<Self>> {
194        self.spi
195            .borrow_mut()
196            .transfer(&mut [data])
197            .map(|b| b[0])
198            .map_err(Error::Transport)
199    }
200
201    /// Receive a byte from the SD card by clocking in an 0xFF byte.
202    fn receive(&self) -> Result<u8, ErrorFor<Self>> {
203        self.transfer(Self::RECEIVE_TRANSFER_TOKEN)
204    }
205
206    /// Send a byte to the SD card.
207    fn send(&self, data: u8) -> Result<(), ErrorFor<Self>> {
208        self.transfer(data).map(|_| ())
209    }
210
211    /// Receive a slice from the SD card.
212    fn receive_slice(&self, data: &mut [u8]) -> Result<(), ErrorFor<Self>> {
213        for byte in data.iter_mut() {
214            *byte = self.receive()?;
215        }
216
217        Ok(())
218    }
219
220    /// Send a slice to the SD card.
221    fn send_slice(&self, data: &[u8]) -> Result<(), ErrorFor<Self>> {
222        for byte in data.iter() {
223            self.send(*byte)?;
224        }
225
226        Ok(())
227    }
228
229    /// Skip byte.
230    fn skip_byte(&self) -> Result<(), ErrorFor<Self>> {
231        self.receive().map(|_| ())
232    }
233
234    /// Wait for token.
235    fn wait_for_token<F: Fn(u8) -> bool>(
236        &self,
237        token_validator: F,
238        error: ErrorFor<Self>,
239    ) -> Result<u8, ErrorFor<Self>> {
240        for _ in 0..Config::CMD_MAX_ATTEMPTS {
241            let token = self.receive()?;
242
243            if token_validator(token) {
244                return Ok(token);
245            }
246
247            Self::delay();
248        }
249
250        Err(error)
251    }
252
253    /// Wait available state of card.
254    fn wait_available_state(&self) -> Result<(), ErrorFor<Self>> {
255        self.wait_for_token(
256            |token| token == tokens::AVAILABLE,
257            Error::TimeoutWaitAvailable,
258        )
259        .map(|_| ())
260    }
261
262    /// Send command implementation.
263    fn send_command_impl(&self, cmd: u8, arg: u32) -> Result<R1Response, ErrorFor<Self>> {
264        self.wait_available_state()?;
265
266        let mut buf = [
267            cmd,
268            (arg >> 24) as u8,
269            (arg >> 16) as u8,
270            (arg >> 8) as u8,
271            arg as u8,
272            0,
273        ];
274        let crc_index = buf.len() - 1;
275
276        buf[crc_index] = (crc7(&buf[..crc_index]) << 1) | 0x01;
277
278        self.send_slice(&buf)?;
279
280        if cmd == commands::CMD12 {
281            self.skip_byte()?;
282        }
283
284        for _ in 0..Config::READ_R1_ATTEMPTS {
285            let r1 = R1Response(self.receive()?);
286
287            if r1.is_valid() {
288                return Ok(r1);
289            }
290        }
291
292        Err(Error::TimeoutCommand(cmd))
293    }
294
295    /// Send command.
296    fn send_command(&self, cmd: u8, arg: u32) -> Result<R1Response, ErrorFor<Self>> {
297        if (cmd & commands::ACMD_FLAG) != 0 {
298            self.send_command_impl(commands::CMD55, 0x0000_0000)?;
299        }
300
301        self.send_command_impl(cmd & !commands::ACMD_FLAG, arg)
302    }
303
304    /// Read data.
305    fn read_data(&self, data: &mut [u8]) -> Result<(), ErrorFor<Self>> {
306        if self.wait_for_token(|token| token != tokens::AVAILABLE, Error::TimeoutReadBuffer)?
307            != tokens::DATA_START_BLOCK
308        {
309            return Err(Error::ReadError);
310        }
311
312        self.receive_slice(data)?;
313
314        let card_crc = (u16::from(self.receive()?) << 8) | u16::from(self.receive()?);
315        let host_crc = crc16(data);
316
317        if card_crc != host_crc {
318            return Err(Error::CrcError(card_crc, host_crc));
319        }
320
321        Ok(())
322    }
323
324    /// Write data.
325    fn write_data(&self, token: u8, data: &[u8]) -> Result<(), ErrorFor<Self>> {
326        let host_crc = crc16(data);
327
328        self.send(token)?;
329        self.send_slice(data)?;
330        self.send((host_crc >> 8) as u8)?;
331        self.send(host_crc as u8)?;
332
333        if (self.receive()? & tokens::DATA_RES_MASK) != tokens::DATA_RES_ACCEPTED {
334            Err(Error::WriteError)
335        } else {
336            Ok(())
337        }
338    }
339
340    /// Enter SD to SPI mode.
341    fn enter_spi_mode(&self) -> Result<(), ErrorFor<Self>> {
342        for i in 0..Config::ENTER_SPI_MODE_ATTEMPTS {
343            info!("Enter to SPI mode for SD, attempt: {}", i + 1);
344
345            match self.send_command(commands::CMD0, 0x0000_0000) {
346                Ok(R1Response::IN_IDLE_STATE) => return Ok(()),
347                Ok(r) => warn!(
348                    "Wrong response from CMD{}: 0b{:02X}",
349                    commands::CMD0 - commands::CMD_BASE,
350                    r.0
351                ),
352                Err(Error::TimeoutCommand(commands::CMD0)) => {}
353                Err(err) => return Err(err),
354            }
355
356            Self::delay();
357        }
358
359        Err(Error::TimeoutCommand(commands::CMD0))
360    }
361
362    /// Enable CRC.
363    fn enable_crc(&self) -> Result<(), ErrorFor<Self>> {
364        info!("Enabling CRC for SD");
365
366        if self.send_command(commands::CMD59, 0x0000_0001)? != R1Response::IN_IDLE_STATE {
367            Err(Error::CantEnableCRC)
368        } else {
369            Ok(())
370        }
371    }
372
373    /// Verify SD Memory Card interface operating condition.
374    fn send_if_cond(&self) -> Result<CardType, ErrorFor<Self>> {
375        info!("Verifing SD Memory Card interface operating condition");
376
377        for _ in 0..Config::CMD_MAX_ATTEMPTS {
378            if self.send_command(commands::CMD8, 0x0000_01AA)? == R1Response::IN_IDLE_AND_ILLEGAL {
379                return Ok(CardType::SD1);
380            }
381
382            self.skip_byte()?;
383            self.skip_byte()?;
384            self.skip_byte()?;
385
386            if self.receive()? == tokens::CMD8_STATUS {
387                return Ok(CardType::SD2);
388            }
389        }
390
391        Err(Error::TimeoutCommand(commands::CMD8))
392    }
393
394    /// Sends host capacity support information and activates.
395    fn send_op_comd(&self, arg: u32) -> Result<(), ErrorFor<Self>> {
396        info!("Sending host capacity support information and activates");
397
398        for _ in 0..Config::CMD_MAX_ATTEMPTS {
399            if self.send_command(commands::ACMD41, arg)? == R1Response::READY_STATE {
400                return Ok(());
401            }
402        }
403
404        Err(Error::TimeoutCommand(commands::ACMD41))
405    }
406
407    /// Check SD type.
408    fn check_type(&self) -> Result<CardType, ErrorFor<Self>> {
409        info!("Checking SD type");
410
411        let mut card_type = self.send_if_cond()?;
412
413        let arg = match card_type {
414            CardType::SD1 => 0x0000_0000,
415            CardType::SD2 | CardType::SDHC => 0x4000_0000,
416        };
417
418        self.send_op_comd(arg)?;
419
420        if card_type == CardType::SD2 {
421            if self.send_command(commands::CMD58, 0x0000_0000)? != R1Response::READY_STATE {
422                return Err(Error::ErrorCommand(commands::CMD58));
423            }
424            if (self.receive()? & tokens::CMD58_OCR) == tokens::CMD58_OCR {
425                card_type = CardType::SDHC;
426            }
427
428            self.skip_byte()?;
429            self.skip_byte()?;
430            self.skip_byte()?;
431        }
432
433        Ok(card_type)
434    }
435
436    /// Read CSD.
437    fn read_csd(&self) -> Result<Csd, ErrorFor<Self>> {
438        let mut csd_data: CsdData = Default::default();
439
440        if self.send_command(commands::CMD9, 0x0000_0000)? != R1Response::READY_STATE {
441            return Err(Error::RegisterReadError);
442        }
443
444        self.read_data(&mut csd_data)?;
445
446        Ok(match self.card_type {
447            CardType::SD1 => Csd::V1(CsdV1::from(csd_data)),
448            CardType::SD2 | CardType::SDHC => Csd::V2(CsdV2::from(csd_data)),
449        })
450    }
451
452    /// Initialize SD.
453    fn init(&mut self) -> Result<(), ErrorFor<Self>> {
454        info!("SD initialize started");
455
456        self.unselect()?;
457
458        for _ in 0..Self::INIT_SET_SIZE {
459            self.send(Self::INIT_SET_VALUE)?;
460        }
461
462        let mut result = self.cs_scope_mut(|s| {
463            s.enter_spi_mode()?;
464            s.enable_crc()?;
465
466            s.card_type = s.check_type()?;
467            s.csd = s.read_csd()?;
468
469            Ok(())
470        });
471
472        self.status = match &result {
473            Ok(_) => {
474                info!(
475                    "SD successfully initialized, version: {}, capacity: {}",
476                    &self.card_type,
477                    defmt::Debug2Format(&self.csd.card_capacity())
478                );
479                Status::default()
480            }
481            Err(err) => {
482                error!("Failed to initialize SD: {}", defmt::Debug2Format(err));
483                result = Err(Error::CardNotFound);
484                StatusFlag::ErrorOccured | StatusFlag::NotInitialized
485            }
486        };
487
488        result
489    }
490}
491
492impl<Spi: Transfer<u8>, Cs: OutputSwitch, Config: SdMmcSpiConfig> DiskioDevice
493    for SdMmcSpi<Spi, Cs, Config>
494where
495    Spi::Error: core::fmt::Debug,
496    Cs::Error: core::fmt::Debug,
497{
498    type HardwareError = Error<Spi::Error, Cs::Error>;
499
500    fn status(&self) -> Status {
501        self.status
502    }
503
504    fn reset(&mut self) {
505        info!("SD reset invoked");
506        self.status = StatusFlag::NotInitialized.into();
507    }
508
509    fn initialize(&mut self) -> Result<(), DiskioError<Self::HardwareError>> {
510        if !self.status.contains(StatusFlag::NotInitialized) {
511            warn!("SD already is initialized");
512            return Err(DiskioError::AlreadyInitialized);
513        }
514
515        self.init().map_err(DiskioError::Hardware)
516    }
517
518    fn read(&self, buf: &mut [u8], lba: Lba) -> Result<(), DiskioError<Self::HardwareError>> {
519        Self::validate_buffer_len(buf.len())?;
520        self.validate_initialized()?;
521
522        let block_count = Self::get_block_count(buf.len());
523        let lba = self.convert_lba(lba);
524
525        self.cs_scope(|s| {
526            if block_count == 1 {
527                s.send_command(commands::CMD17, lba)?;
528                s.read_data(buf)?;
529            } else {
530                s.send_command(commands::CMD18, lba)?;
531                for chunk in buf.chunks_mut(BLOCK_SIZE) {
532                    s.read_data(chunk)?;
533                }
534                s.send_command(commands::CMD12, 0x0000_0000)?;
535            }
536
537            Ok(())
538        })
539        .map_err(DiskioError::Hardware)
540    }
541
542    fn write(&self, buf: &[u8], lba: Lba) -> Result<(), DiskioError<Self::HardwareError>> {
543        Self::validate_buffer_len(buf.len())?;
544        self.validate_initialized()?;
545
546        let block_count = Self::get_block_count(buf.len());
547        let lba = self.convert_lba(lba);
548
549        self.cs_scope(|s| {
550            if block_count == 1 {
551                s.send_command(commands::CMD24, lba)?;
552                s.write_data(tokens::DATA_START_BLOCK, buf)?;
553                s.wait_available_state()?;
554                if s.send_command(commands::CMD13, 0x0000_0000)? != R1Response::READY_STATE {
555                    return Err(Error::WriteError);
556                }
557                if s.receive()? != R1Response::READY_STATE.0 {
558                    return Err(Error::WriteError);
559                }
560            } else {
561                s.send_command(commands::CMD25, lba)?;
562                for block in buf.chunks(BLOCK_SIZE) {
563                    s.wait_available_state()?;
564                    self.write_data(tokens::WRITE_MULTIPLE, block)?;
565                }
566                s.wait_available_state()?;
567                s.send(tokens::STOP_TRAN)?;
568            }
569
570            Ok(())
571        })
572        .map_err(DiskioError::Hardware)
573    }
574
575    fn ioctl(&self, cmd: IoctlCmd) -> Result<(), DiskioError<Self::HardwareError>> {
576        match cmd {
577            IoctlCmd::CtrlSync => self.wait_available_state().map_err(DiskioError::Hardware),
578            IoctlCmd::GetBlockSize(block_size) => {
579                *block_size = BLOCK_SIZE;
580                Ok(())
581            }
582            _ => Err(DiskioError::NotSupported),
583        }
584    }
585}