1#![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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum Error<T, S> {
36 Transport(T),
38 SelectError(S),
40 CantEnableCRC,
42 TimeoutReadBuffer,
44 TimeoutWaitAvailable,
46 TimeoutCommand(u8),
48 ErrorCommand(u8),
50 RegisterReadError,
52 CrcError(u16, u16),
54 ReadError,
56 WriteError,
58 BadState,
60 CardNotFound,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Format)]
66pub enum CardType {
67 SD1,
68 SD2,
69 SDHC,
70}
71
72type ErrorFor<T> = <T as DiskioDevice>::HardwareError;
74
75pub 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 const INIT_SET_VALUE: u8 = 0xFF;
96 const INIT_SET_SIZE: usize = 10;
98 const RECEIVE_TRANSFER_TOKEN: u8 = 0xFF;
100
101 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 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 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 fn get_block_count(buf_len: usize) -> usize {
140 buf_len / BLOCK_SIZE
141 }
142
143 fn delay() {
145 for i in 0..Config::DELAY_DUMMY_CYCLES {
146 unsafe { core::ptr::read_volatile(&i) };
147 }
148 }
149
150 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 fn select(&self) -> Result<(), ErrorFor<Self>> {
160 self.cs.borrow_mut().on().map_err(Error::SelectError)
161 }
162
163 fn unselect(&self) -> Result<(), ErrorFor<Self>> {
165 self.cs.borrow_mut().off().map_err(Error::SelectError)
166 }
167
168 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 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 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 fn receive(&self) -> Result<u8, ErrorFor<Self>> {
203 self.transfer(Self::RECEIVE_TRANSFER_TOKEN)
204 }
205
206 fn send(&self, data: u8) -> Result<(), ErrorFor<Self>> {
208 self.transfer(data).map(|_| ())
209 }
210
211 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 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 fn skip_byte(&self) -> Result<(), ErrorFor<Self>> {
231 self.receive().map(|_| ())
232 }
233
234 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 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 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 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 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 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 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 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 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 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 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 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 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}