1use embedded_hal::{delay::DelayNs, spi::SpiDevice};
9use log::{debug, info, warn};
10
11use crate::{
12 cmd::Command,
13 common::{block_addr_of, crc16_ccitt},
14 error::{Error, ErrorContext, Phase},
15 response::{
16 CidResponse, CsdResponse, IfCondResponse, OcrResponse, R1Response, Response, ResponseType,
17 SwitchStatus,
18 },
19};
20
21const TOKEN_START_BLOCK: u8 = 0xFE;
23const TOKEN_START_MULTI_BLOCK: u8 = 0xFC;
24const TOKEN_STOP_TRAN: u8 = 0xFD;
25
26pub trait SpiTransport {
28 fn select(&mut self) -> Result<(), Error> {
30 Ok(())
31 }
32
33 fn deselect(&mut self) -> Result<(), Error> {
35 Ok(())
36 }
37
38 fn transfer_byte(&mut self, byte: u8) -> Result<u8, Error>;
40 fn send_byte(&mut self, byte: u8) -> Result<(), Error> {
42 self.transfer_byte(byte)?;
43 Ok(())
44 }
45 fn clock(&mut self) -> Result<(), Error> {
47 self.transfer_byte(0xFF)?;
48 Ok(())
49 }
50}
51
52impl<SPI> SpiTransport for SpiDeviceWrapper<SPI>
54where
55 SPI: SpiDevice<u8>,
56{
57 fn transfer_byte(&mut self, byte: u8) -> Result<u8, Error> {
58 let mut buf = [byte];
59 self.spi
60 .transfer(&mut buf, &[byte])
61 .map_err(|_| Error::BusError(ErrorContext::new(Phase::Unspecified)))?;
62 Ok(buf[0])
63 }
64}
65
66pub struct SpiDeviceWrapper<SPI> {
68 spi: SPI,
69}
70
71impl<SPI> SpiDeviceWrapper<SPI> {
72 pub fn new(spi: SPI) -> Self {
73 Self { spi }
74 }
75}
76
77pub struct SpiSdmmc<T: SpiTransport, D: DelayNs> {
79 transport: T,
80 delay: D,
81 sd_v2: bool,
82 high_capacity: bool,
83 verify_data_crc: bool,
84}
85
86impl<T: SpiTransport, D: DelayNs> SpiSdmmc<T, D> {
87 const RESPONSE_TIMEOUT_US: u32 = 100_000;
89 const READ_TIMEOUT_US: u32 = 100_000;
91 const WRITE_BUSY_TIMEOUT_US: u32 = 250_000;
94 const INIT_TIMEOUT_US: u32 = 1_000_000;
96 const POLL_INTERVAL_US: u32 = 50;
98
99 pub fn new(transport: T, delay: D) -> Self {
100 Self {
101 transport,
102 delay,
103 sd_v2: false,
104 high_capacity: false,
105 verify_data_crc: true,
106 }
107 }
108
109 pub fn set_verify_data_crc(&mut self, on: bool) {
117 self.verify_data_crc = on;
118 }
119
120 pub fn init(&mut self) -> Result<CardInfo, Error> {
131 debug!("spi: init starting");
132 for _ in 0..10 {
133 self.transport.clock()?;
134 }
135
136 self.send_command(&crate::cmd::CMD0)?;
137 self.sd_v2 = self.check_cmd8()?;
138 debug!("spi: sd_v2={}", self.sd_v2);
139 self.wait_ready()?;
140
141 let ocr = self.read_ocr()?;
142 self.high_capacity = ocr.ccs() || self.sd_v2;
143 let csd = self.read_csd()?;
144 let capacity_blocks = csd.capacity_blocks();
145 let cid = self.read_cid().ok();
146 if !self.high_capacity {
147 self.send_command(&crate::cmd::cmd16(512))?;
148 }
149
150 info!(
151 "spi: init done sd_v2={} high_capacity={} ocr={:#x}",
152 self.sd_v2, self.high_capacity, ocr.raw
153 );
154 Ok(CardInfo {
155 sd_v2: self.sd_v2,
156 high_capacity: self.high_capacity,
157 ocr: ocr.raw,
158 capacity_blocks,
159 cid,
160 })
161 }
162
163 fn check_cmd8(&mut self) -> Result<bool, Error> {
164 let cmd = crate::cmd::cmd8(0x01, 0xAA);
165 match self.send_command_raw(&cmd) {
166 Ok(Response::R7(resp)) => Ok(resp.verify(0x01, 0xAA)),
167 Ok(Response::R1(resp)) if resp.illegal_command() => Ok(false),
168 Ok(_) => Err(Error::BadResponse(ErrorContext::for_cmd(Phase::Init, 8))),
169 Err(Error::Timeout(_)) => Ok(false),
170 Err(e) => Err(e),
171 }
172 }
173
174 fn wait_ready(&mut self) -> Result<(), Error> {
175 let mut elapsed = 0u32;
176 loop {
177 let cmd55 = crate::cmd::cmd55(0);
178 self.send_command(&cmd55)?;
179
180 let acmd41 = crate::cmd::cmd41(self.sd_v2, 0xFF8000).with_resp_type(ResponseType::R1);
183 match self.send_command_raw(&acmd41)? {
184 Response::R1(r1) => {
185 if !r1.idle() {
186 return Ok(());
187 }
188 }
189 _ => return Err(Error::BadResponse(ErrorContext::for_cmd(Phase::Init, 41))),
190 }
191
192 if elapsed >= Self::INIT_TIMEOUT_US {
193 warn!("spi: ACMD41 timed out after {}us", elapsed);
194 return Err(Error::Timeout(ErrorContext::for_cmd(Phase::Init, 41)));
195 }
196 self.delay.delay_us(1_000);
197 elapsed = elapsed.saturating_add(1_000);
198 }
199 }
200
201 fn read_ocr(&mut self) -> Result<OcrResponse, Error> {
202 match self.send_command_raw(&crate::cmd::CMD58)? {
203 Response::R3(ocr) => Ok(ocr),
204 _ => Err(Error::BadResponse(ErrorContext::for_cmd(Phase::Init, 58))),
205 }
206 }
207
208 fn read_csd(&mut self) -> Result<CsdResponse, Error> {
209 match self.send_command_raw(&crate::cmd::cmd9(0))? {
210 Response::R2(raw) => Ok(CsdResponse::from_raw(raw)),
211 _ => Err(Error::BadResponse(ErrorContext::for_cmd(Phase::Init, 9))),
212 }
213 }
214
215 fn read_cid(&mut self) -> Result<CidResponse, Error> {
216 match self.send_command_raw(&crate::cmd::cmd10(0))? {
217 Response::R2(raw) => Ok(CidResponse::from_raw(raw)),
218 _ => Err(Error::BadResponse(ErrorContext::for_cmd(Phase::Init, 10))),
219 }
220 }
221
222 pub fn read_block(&mut self, addr: u32, buf: &mut [u8; 512]) -> Result<(), Error> {
226 let block_addr = block_addr_of(addr, self.high_capacity);
227 let cmd = crate::cmd::cmd17(block_addr);
228 self.send_command(&cmd)?;
229 self.read_data_block(buf)
230 }
231
232 pub fn write_block(&mut self, addr: u32, buf: &[u8; 512]) -> Result<(), Error> {
234 let block_addr = block_addr_of(addr, self.high_capacity);
235 let cmd = crate::cmd::cmd24(block_addr);
236 self.send_command(&cmd)?;
237 self.write_data_block(buf)
238 }
239
240 pub fn read_blocks<F>(&mut self, addr: u32, count: u32, mut handler: F) -> Result<(), Error>
242 where
243 F: FnMut(u32, &[u8; 512]),
244 {
245 let block_addr = block_addr_of(addr, self.high_capacity);
246 let cmd = crate::cmd::cmd18(block_addr);
247 self.send_command(&cmd)?;
248
249 let mut buf = [0u8; 512];
250 for i in 0..count {
251 self.read_data_block(&mut buf)?;
252 handler(addr + i, &buf);
253 }
254
255 self.send_command(&crate::cmd::CMD12)?;
256 self.wait_not_busy()?;
257 self.transport.deselect()?;
258 Ok(())
259 }
260
261 pub fn write_blocks(&mut self, addr: u32, blocks: &[[u8; 512]]) -> Result<(), Error> {
263 let block_addr = block_addr_of(addr, self.high_capacity);
264 let cmd = crate::cmd::cmd25(block_addr);
265 self.send_command(&cmd)?;
266
267 for block in blocks {
268 self.transport.send_byte(TOKEN_START_MULTI_BLOCK)?;
269 for &b in block {
270 self.transport.send_byte(b)?;
271 }
272 let crc = crc16_ccitt(block).to_be_bytes();
273 self.transport.send_byte(crc[0])?;
274 self.transport.send_byte(crc[1])?;
275
276 let resp = self.wait_for_response(Self::RESPONSE_TIMEOUT_US)?;
277 if (resp & 0x1F) != 0x05 {
278 return Err(Error::WriteError(ErrorContext::for_cmd(
279 Phase::DataWrite,
280 25,
281 )));
282 }
283 self.wait_not_busy()?;
284 }
285
286 self.transport.send_byte(TOKEN_STOP_TRAN)?;
287 self.transport.clock()?;
288 self.wait_not_busy()?;
289 self.transport.deselect()?;
290 Ok(())
291 }
292
293 fn send_command(&mut self, cmd: &Command) -> Result<R1Response, Error> {
296 let resp = self.send_command_raw(cmd)?;
297 match resp {
298 Response::R1(r1) | Response::R1b(r1) => Ok(r1),
299 _ => Err(Error::BadResponse(ErrorContext::for_cmd(
300 Phase::ResponseWait,
301 cmd.cmd,
302 ))),
303 }
304 }
305
306 fn send_command_raw(&mut self, cmd: &Command) -> Result<Response, Error> {
307 self.transport.select()?;
308 let bytes = cmd.to_spi_bytes();
309 for &b in &bytes {
310 self.transport.send_byte(b)?;
311 }
312
313 let response = match cmd.resp_type {
314 ResponseType::None => {
315 let r1 = self.read_r1()?;
316 Ok(Response::R1(r1))
317 }
318 ResponseType::R1 => {
319 let r1 = self.read_r1()?;
320 Ok(Response::R1(r1))
321 }
322 ResponseType::R1b => {
323 let r1 = self.read_r1()?;
324 self.wait_not_busy()?;
325 Ok(Response::R1b(r1))
326 }
327 ResponseType::R3 => {
328 self.read_r1()?;
329 let mut ocr = [0u8; 4];
330 for b in &mut ocr {
331 *b = self.transport.transfer_byte(0xFF)?;
332 }
333 let raw = u32::from_be_bytes(ocr);
334 Ok(Response::R3(OcrResponse::from_raw(raw)))
335 }
336 ResponseType::R7 => {
337 let r1 = self.read_r1()?;
338 if r1.illegal_command() {
339 return Ok(Response::R1(r1));
340 }
341 let mut data = [0u8; 4];
342 for b in &mut data {
343 *b = self.transport.transfer_byte(0xFF)?;
344 }
345 let raw = u32::from_be_bytes(data);
346 Ok(Response::R7(IfCondResponse::from_raw(raw)))
347 }
348 ResponseType::R2 => {
349 let r1 = self.read_r1()?;
350 if r1.raw != 0 {
351 return Ok(Response::R1(r1));
352 }
353 let mut buf = [0u8; 16];
354 self.read_data_register(&mut buf)?;
355 Ok(Response::R2(buf))
356 }
357 _ => {
358 let r1 = self.read_r1()?;
359 Ok(Response::R1(r1))
360 }
361 };
362 if cmd.data_direction().is_none() {
364 self.transport.deselect()?;
365 }
366 response
367 }
368
369 fn read_r1(&mut self) -> Result<R1Response, Error> {
370 let raw = self.wait_for_response(Self::RESPONSE_TIMEOUT_US)?;
371 R1Response::from_spi_byte(raw)
372 }
373
374 fn wait_for_response(&mut self, timeout_us: u32) -> Result<u8, Error> {
376 let mut elapsed = 0u32;
377 loop {
378 let b = self.transport.transfer_byte(0xFF)?;
379 if b != 0xFF {
380 return Ok(b);
381 }
382 if elapsed >= timeout_us {
383 return Err(Error::Timeout(ErrorContext::new(Phase::ResponseWait)));
384 }
385 self.delay.delay_us(Self::POLL_INTERVAL_US);
386 elapsed = elapsed.saturating_add(Self::POLL_INTERVAL_US);
387 }
388 }
389
390 fn read_data_into(&mut self, buf: &mut [u8]) -> Result<(), Error> {
392 let mut elapsed = 0u32;
393 loop {
394 let b = self.transport.transfer_byte(0xFF)?;
395 if b == TOKEN_START_BLOCK {
396 break;
397 }
398 if b != 0xFF {
399 return Err(Error::ReadError(ErrorContext::new(Phase::DataRead)));
400 }
401 if elapsed >= Self::READ_TIMEOUT_US {
402 return Err(Error::Timeout(ErrorContext::new(Phase::DataRead)));
403 }
404 self.delay.delay_us(Self::POLL_INTERVAL_US);
405 elapsed = elapsed.saturating_add(Self::POLL_INTERVAL_US);
406 }
407
408 for b in buf.iter_mut() {
409 *b = self.transport.transfer_byte(0xFF)?;
410 }
411
412 let crc_high = self.transport.transfer_byte(0xFF)?;
413 let crc_low = self.transport.transfer_byte(0xFF)?;
414 if self.verify_data_crc {
415 let received = u16::from_be_bytes([crc_high, crc_low]);
416 let computed = crc16_ccitt(buf);
417 if received != computed {
418 warn!(
419 "spi: data CRC mismatch (received={:#x} computed={:#x})",
420 received, computed
421 );
422 return Err(Error::Crc(ErrorContext::new(Phase::DataRead)));
423 }
424 }
425 Ok(())
426 }
427
428 fn read_data_register(&mut self, buf: &mut [u8; 16]) -> Result<(), Error> {
429 self.read_data_into(buf)
430 }
431
432 fn read_data_block(&mut self, buf: &mut [u8; 512]) -> Result<(), Error> {
433 self.read_data_into(buf)?;
434 self.transport.deselect()?;
435 Ok(())
436 }
437
438 fn write_data_block(&mut self, buf: &[u8; 512]) -> Result<(), Error> {
439 self.transport.send_byte(TOKEN_START_BLOCK)?;
440 for &b in buf {
441 self.transport.send_byte(b)?;
442 }
443 let crc = crc16_ccitt(buf).to_be_bytes();
444 self.transport.send_byte(crc[0])?;
445 self.transport.send_byte(crc[1])?;
446
447 let resp = self.wait_for_response(Self::RESPONSE_TIMEOUT_US)?;
448 if (resp & 0x1F) != 0x05 {
449 return Err(Error::WriteError(ErrorContext::new(Phase::DataWrite)));
450 }
451
452 self.wait_not_busy()?;
453 self.transport.deselect()?;
454 Ok(())
455 }
456
457 fn wait_not_busy(&mut self) -> Result<(), Error> {
458 let mut elapsed = 0u32;
459 loop {
460 if self.transport.transfer_byte(0xFF)? == 0xFF {
461 return Ok(());
462 }
463 if elapsed >= Self::WRITE_BUSY_TIMEOUT_US {
464 return Err(Error::Timeout(ErrorContext::new(Phase::BusyWait)));
465 }
466 self.delay.delay_us(Self::POLL_INTERVAL_US);
467 elapsed = elapsed.saturating_add(Self::POLL_INTERVAL_US);
468 }
469 }
470
471 pub fn switch_function(&mut self, cmd: &Command) -> Result<SwitchStatus, Error> {
473 self.transport.select()?;
477 let bytes = cmd.to_spi_bytes();
478 for &b in &bytes {
479 self.transport.send_byte(b)?;
480 }
481 let _r1 = self.read_r1()?;
482
483 let mut buf = [0u8; 64];
484 self.read_data_into(&mut buf)?;
485 self.transport.deselect()?;
486 Ok(SwitchStatus::from_raw(buf))
487 }
488
489 pub fn switch_to_high_speed(&mut self) -> Result<bool, Error> {
496 let status = self.switch_function(&crate::cmd::cmd6_high_speed(true))?;
497 let active = status.high_speed_active();
498 if active {
499 info!("spi: switched to high-speed mode");
500 } else {
501 warn!("spi: high-speed switch did not take effect");
502 }
503 Ok(active)
504 }
505}
506
507#[derive(Debug, Clone, Copy)]
509pub struct CardInfo {
510 pub sd_v2: bool,
511 pub high_capacity: bool,
512 pub ocr: u32,
513 pub capacity_blocks: Option<u64>,
516 pub cid: Option<CidResponse>,
519}
520
521#[cfg(test)]
522mod tests {
523 extern crate std;
524
525 use std::vec::Vec;
526
527 use super::*;
528 use crate::cmd;
529
530 struct NullDelay;
533
534 impl DelayNs for NullDelay {
535 fn delay_ns(&mut self, _ns: u32) {}
536 }
537
538 struct ScriptedTransport {
539 rx: Vec<u8>,
540 tx: Vec<u8>,
541 }
542
543 impl ScriptedTransport {
544 fn new(rx: Vec<u8>) -> Self {
545 Self { rx, tx: Vec::new() }
546 }
547
548 fn push_ignored(rx: &mut Vec<u8>, count: usize) {
549 for _ in 0..count {
550 rx.push(0xFF);
551 }
552 }
553
554 fn push_command_response(rx: &mut Vec<u8>, r1: u8, extra: &[u8]) {
555 Self::push_ignored(rx, 6);
556 rx.push(r1);
557 rx.extend_from_slice(extra);
558 }
559
560 fn tx_contains(&self, bytes: &[u8]) -> bool {
561 self.tx.windows(bytes.len()).any(|window| window == bytes)
562 }
563 }
564
565 impl SpiTransport for ScriptedTransport {
566 fn transfer_byte(&mut self, byte: u8) -> Result<u8, Error> {
567 self.tx.push(byte);
568 if self.rx.is_empty() {
569 return Err(Error::Timeout(ErrorContext::default()));
570 }
571 Ok(self.rx.remove(0))
572 }
573 }
574
575 fn driver(rx: Vec<u8>) -> SpiSdmmc<ScriptedTransport, NullDelay> {
576 SpiSdmmc::new(ScriptedTransport::new(rx), NullDelay)
577 }
578
579 fn push_csd_v2_response(rx: &mut Vec<u8>) {
580 ScriptedTransport::push_command_response(rx, 0x00, &[]);
582 rx.push(TOKEN_START_BLOCK);
583 let mut csd = [0u8; 16];
584 csd[0] = 0x40; csd[7] = 0x00;
586 csd[8] = 0x0F;
587 csd[9] = 0x0F;
588 rx.extend_from_slice(&csd);
589 rx.extend_from_slice(&crc16_ccitt(&csd).to_be_bytes());
590 }
591
592 fn push_cid_response(rx: &mut Vec<u8>) {
593 ScriptedTransport::push_command_response(rx, 0x00, &[]);
594 rx.push(TOKEN_START_BLOCK);
595 let mut cid = [0u8; 16];
596 cid[0] = 0x03;
597 cid[1] = b'S';
598 cid[2] = b'D';
599 cid[3] = b'A';
600 cid[4] = b'B';
601 cid[5] = b'C';
602 cid[6] = b'1';
603 cid[7] = b'2';
604 rx.extend_from_slice(&cid);
605 rx.extend_from_slice(&crc16_ccitt(&cid).to_be_bytes());
606 }
607
608 #[test]
609 fn init_polls_acmd41_until_spi_r1_leaves_idle() {
610 let mut rx = Vec::new();
611 ScriptedTransport::push_ignored(&mut rx, 10);
612 ScriptedTransport::push_command_response(&mut rx, 0x01, &[]);
613 ScriptedTransport::push_command_response(&mut rx, 0x01, &[0x00, 0x00, 0x01, 0xAA]);
614 ScriptedTransport::push_command_response(&mut rx, 0x01, &[]);
615 ScriptedTransport::push_command_response(&mut rx, 0x01, &[]);
616 ScriptedTransport::push_command_response(&mut rx, 0x01, &[]);
617 ScriptedTransport::push_command_response(&mut rx, 0x00, &[]);
618 ScriptedTransport::push_command_response(&mut rx, 0x00, &[0xC0, 0xFF, 0x80, 0x00]);
619 push_csd_v2_response(&mut rx);
620 push_cid_response(&mut rx);
621
622 let mut driver = driver(rx);
623 let info = driver.init().unwrap();
624
625 assert!(info.sd_v2);
626 assert!(info.high_capacity);
627 assert_eq!(info.ocr, 0xC0FF_8000);
628 assert_eq!(info.capacity_blocks, Some((0x0F0F + 1) * 1024));
629 let cid = info.cid.expect("CID parsed during init");
630 assert_eq!(cid.manufacturer_id(), 0x03);
631 assert_eq!(&cid.oem_id(), b"SD");
632 assert_eq!(&cid.product_name(), b"ABC12");
633 assert!(driver.transport.tx_contains(&cmd::CMD0.to_spi_bytes()));
634 assert!(
635 driver
636 .transport
637 .tx_contains(&cmd::cmd8(0x01, 0xAA).to_spi_bytes())
638 );
639 assert!(driver.transport.tx_contains(&cmd::CMD58.to_spi_bytes()));
640 assert!(driver.transport.tx_contains(&cmd::cmd10(0).to_spi_bytes()));
641 }
642
643 #[test]
644 fn read_block_times_out_when_start_token_never_arrives() {
645 let mut rx = Vec::new();
646 ScriptedTransport::push_command_response(&mut rx, 0x00, &[]);
647 ScriptedTransport::push_ignored(&mut rx, 10_000);
650 rx.extend_from_slice(&[0xAA; 512]);
651 rx.extend_from_slice(&[0xFF; 2]);
652
653 let mut driver = driver(rx);
654 driver.high_capacity = true;
655 let mut buf = [0u8; 512];
656
657 assert!(matches!(
658 driver.read_block(7, &mut buf),
659 Err(Error::Timeout(_))
660 ));
661 assert!(driver.transport.tx_contains(&cmd::cmd17(7).to_spi_bytes()));
662 }
663
664 #[test]
665 fn read_block_returns_payload_when_crc_matches() {
666 let mut payload = [0u8; 512];
667 for (i, b) in payload.iter_mut().enumerate() {
668 *b = (i & 0xFF) as u8;
669 }
670 let crc = crc16_ccitt(&payload).to_be_bytes();
671
672 let mut rx = Vec::new();
673 ScriptedTransport::push_command_response(&mut rx, 0x00, &[]);
674 rx.push(0xFF);
675 rx.push(TOKEN_START_BLOCK);
676 rx.extend_from_slice(&payload);
677 rx.extend_from_slice(&crc);
678
679 let mut driver = driver(rx);
680 driver.high_capacity = true;
681 let mut buf = [0u8; 512];
682 driver.read_block(0, &mut buf).unwrap();
683 assert_eq!(&buf[..], &payload[..]);
684 }
685
686 #[test]
687 fn read_block_reports_crc_error_when_trailer_mismatches() {
688 let payload = [0u8; 512];
689 let mut bad_crc = crc16_ccitt(&payload).to_be_bytes();
690 bad_crc[0] ^= 0xFF;
691
692 let mut rx = Vec::new();
693 ScriptedTransport::push_command_response(&mut rx, 0x00, &[]);
694 rx.push(0xFF);
695 rx.push(TOKEN_START_BLOCK);
696 rx.extend_from_slice(&payload);
697 rx.extend_from_slice(&bad_crc);
698
699 let mut driver = driver(rx);
700 driver.high_capacity = true;
701 let mut buf = [0u8; 512];
702 assert!(matches!(driver.read_block(0, &mut buf), Err(Error::Crc(_))));
703 }
704
705 #[test]
706 fn write_block_emits_correct_crc16_after_payload() {
707 let mut rx = Vec::new();
708 ScriptedTransport::push_command_response(&mut rx, 0x00, &[]);
710 ScriptedTransport::push_ignored(&mut rx, 1 + 512 + 2);
713 rx.push(0x05); rx.push(0xFF); let mut driver = driver(rx);
717 driver.high_capacity = true;
718 let payload = [0xA5u8; 512];
719 driver.write_block(0, &payload).unwrap();
720
721 let crc = crc16_ccitt(&payload).to_be_bytes();
722 assert!(driver.transport.tx_contains(&payload));
723 assert!(driver.transport.tx_contains(&crc));
724 }
725
726 #[test]
727 fn switch_to_high_speed_returns_true_when_status_confirms() {
728 let mut status = [0u8; 64];
729 status[16] = 0x01; let crc = crc16_ccitt(&status).to_be_bytes();
731
732 let mut rx = Vec::new();
733 ScriptedTransport::push_command_response(&mut rx, 0x00, &[]);
735 rx.push(0xFF); rx.push(TOKEN_START_BLOCK);
737 rx.extend_from_slice(&status);
738 rx.extend_from_slice(&crc);
739
740 let mut driver = driver(rx);
741 let active = driver.switch_to_high_speed().unwrap();
742 assert!(active);
743
744 let cmd = cmd::cmd6_high_speed(true);
746 assert!(driver.transport.tx_contains(&cmd.to_spi_bytes()));
747 }
748
749 #[test]
750 fn switch_to_high_speed_returns_false_when_card_keeps_default() {
751 let status = [0u8; 64]; let crc = crc16_ccitt(&status).to_be_bytes();
753
754 let mut rx = Vec::new();
755 ScriptedTransport::push_command_response(&mut rx, 0x00, &[]);
756 rx.push(0xFF);
757 rx.push(TOKEN_START_BLOCK);
758 rx.extend_from_slice(&status);
759 rx.extend_from_slice(&crc);
760
761 let mut driver = driver(rx);
762 let active = driver.switch_to_high_speed().unwrap();
763 assert!(!active);
764 }
765}