-
Notifications
You must be signed in to change notification settings - Fork 27
/
SDBlockDevice.cpp
1116 lines (967 loc) · 35.9 KB
/
SDBlockDevice.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Introduction
* ------------
* SD and MMC cards support a number of interfaces, but common to them all
* is one based on SPI. Since we already have the mbed SPI Interface, it will
* be used for SD cards.
*
* The main reference I'm using is Chapter 7, "SPI Mode" of:
* http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
*
* SPI Startup
* -----------
* The SD card powers up in SD mode. The start-up procedure is complicated
* by the requirement to support older SDCards in a backwards compatible
* way with the new higher capacity variants SDHC and SDHC.
*
* The following figures from the specification with associated text describe
* the SPI mode initialisation process:
* - Figure 7-1: SD Memory Card State Diagram (SPI mode)
* - Figure 7-2: SPI Mode Initialization Flow
*
* Firstly, a low initial clock should be selected (in the range of 100-
* 400kHZ). After initialisation has been completed, the switch to a
* higher clock speed can be made (e.g. 1MHz). Newer cards will support
* higher speeds than the default _transfer_sck defined here.
*
* Next, note the following from the SDCard specification (note to
* Figure 7-1):
*
* In any of the cases CMD1 is not recommended because it may be difficult for the host
* to distinguish between MultiMediaCard and SD Memory Card
*
* Hence CMD1 is not used for the initialisation sequence.
*
* The SPI interface mode is selected by asserting CS low and sending the
* reset command (CMD0). The card will respond with a (R1) response.
* In practice many cards initially respond with 0xff or invalid data
* which is ignored. Data is read until a valid response is received
* or the number of re-reads has exceeded a maximim count. If a valid
* response is not received then the CMD0 can be retried. This
* has been found to successfully initialise cards where the SPI master
* (on MCU) has been reset but the SDCard has not, so the first
* CMD0 may be lost.
*
* CMD8 is optionally sent to determine the voltage range supported, and
* indirectly determine whether it is a version 1.x SD/non-SD card or
* version 2.x. I'll just ignore this for now.
*
* ACMD41 is repeatedly issued to initialise the card, until "in idle"
* (bit 0) of the R1 response goes to '0', indicating it is initialised.
*
* You should also indicate whether the host supports High Capicity cards,
* and check whether the card is high capacity - i'll also ignore this
*
* SPI Protocol
* ------------
* The SD SPI protocol is based on transactions made up of 8-bit words, with
* the host starting every bus transaction by asserting the CS signal low. The
* card always responds to commands, data blocks and errors.
*
* The protocol supports a CRC, but by default it is off (except for the
* first reset CMD0, where the CRC can just be pre-calculated, and CMD8)
* I'll leave the CRC off I think!
*
* Standard capacity cards have variable data block sizes, whereas High
* Capacity cards fix the size of data block to 512 bytes. I'll therefore
* just always use the Standard Capacity cards with a block size of 512 bytes.
* This is set with CMD16.
*
* You can read and write single blocks (CMD17, CMD25) or multiple blocks
* (CMD18, CMD25). For simplicity, I'll just use single block accesses. When
* the card gets a read command, it responds with a response token, and then
* a data token or an error.
*
* SPI Command Format
* ------------------
* Commands are 6-bytes long, containing the command, 32-bit argument, and CRC.
*
* +---------------+------------+------------+-----------+----------+--------------+
* | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 |
* +---------------+------------+------------+-----------+----------+--------------+
*
* As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95)
*
* All Application Specific commands shall be preceded with APP_CMD (CMD55).
*
* SPI Response Format
* -------------------
* The main response format (R1) is a status byte (normally zero). Key flags:
* idle - 1 if the card is in an idle state/initialising
* cmd - 1 if an illegal command code was detected
*
* +-------------------------------------------------+
* R1 | 0 | arg | addr | seq | crc | cmd | erase | idle |
* +-------------------------------------------------+
*
* R1b is the same, except it is followed by a busy signal (zeros) until
* the first non-zero byte when it is ready again.
*
* Data Response Token
* -------------------
* Every data block written to the card is acknowledged by a byte
* response token
*
* +----------------------+
* | xxx | 0 | status | 1 |
* +----------------------+
* 010 - OK!
* 101 - CRC Error
* 110 - Write Error
*
* Single Block Read and Write
* ---------------------------
*
* Block transfers have a byte header, followed by the data, followed
* by a 16-bit CRC. In our case, the data will always be 512 bytes.
*
* +------+---------+---------+- - - -+---------+-----------+----------+
* | 0xFE | data[0] | data[1] | | data[n] | crc[15:8] | crc[7:0] |
* +------+---------+---------+- - - -+---------+-----------+----------+
*/
/* If the target has no SPI support then SDCard is not supported */
#ifdef DEVICE_SPI
#include "SDBlockDevice.h"
#include "mbed_debug.h"
#include <errno.h>
/* Required version: 5.9.0 and above */
#if defined(MBED_MAJOR_VERSION) && MBED_MAJOR_VERSION >= 5
#if (MBED_VERSION < MBED_ENCODE_VERSION(5,9,0))
#error "Incompatible mbed-os version detected! Required 5.9.0 and above"
#endif
#else
#warning "mbed-os version 5.9.0 or above required"
#endif
/* Started from version 5.10.0 SDBlockDevice external repo is deprecated.
please use the SDBlockDevice component inside mbed-os.*/
#if defined(MBED_MAJOR_VERSION) && MBED_MAJOR_VERSION >= 5 && (MBED_VERSION >= MBED_ENCODE_VERSION(5,10,0))
#error "Started from version 5.10.0 SDBlockDevice external repo is deprecated. please use the SDBlockDevice component inside mbed-os."
#endif
#ifndef MBED_CONF_SD_CMD_TIMEOUT
#define MBED_CONF_SD_CMD_TIMEOUT 5000 /*!< Timeout in ms for response */
#endif
#ifndef MBED_CONF_SD_CMD0_IDLE_STATE_RETRIES
#define MBED_CONF_SD_CMD0_IDLE_STATE_RETRIES 5 /*!< Number of retries for sending CMDO */
#endif
#ifndef MBED_CONF_SD_INIT_FREQUENCY
#define MBED_CONF_SD_INIT_FREQUENCY 100000 /*!< Initialization frequency Range (100KHz-400KHz) */
#endif
#define SD_COMMAND_TIMEOUT MBED_CONF_SD_CMD_TIMEOUT
#define SD_CMD0_GO_IDLE_STATE_RETRIES MBED_CONF_SD_CMD0_IDLE_STATE_RETRIES
#define SD_DBG 0 /*!< 1 - Enable debugging */
#define SD_CMD_TRACE 0 /*!< 1 - Enable SD command tracing */
#define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */
#define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */
#define SD_BLOCK_DEVICE_ERROR_PARAMETER -5003 /*!< invalid parameter */
#define SD_BLOCK_DEVICE_ERROR_NO_INIT -5004 /*!< uninitialized */
#define SD_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */
#define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */
#define SD_BLOCK_DEVICE_ERROR_UNUSABLE -5007 /*!< unusable card */
#define SD_BLOCK_DEVICE_ERROR_NO_RESPONSE -5008 /*!< No response from device */
#define SD_BLOCK_DEVICE_ERROR_CRC -5009 /*!< CRC error */
#define SD_BLOCK_DEVICE_ERROR_ERASE -5010 /*!< Erase error: reset/sequence */
#define SD_BLOCK_DEVICE_ERROR_WRITE -5011 /*!< SPI Write error: !SPI_DATA_ACCEPTED */
#define BLOCK_SIZE_HC 512 /*!< Block size supported for SD card is 512 bytes */
#define WRITE_BL_PARTIAL 0 /*!< Partial block write - Not supported */
#define SPI_CMD(x) (0x40 | (x & 0x3f))
/* R1 Response Format */
#define R1_NO_RESPONSE (0xFF)
#define R1_RESPONSE_RECV (0x80)
#define R1_IDLE_STATE (1 << 0)
#define R1_ERASE_RESET (1 << 1)
#define R1_ILLEGAL_COMMAND (1 << 2)
#define R1_COM_CRC_ERROR (1 << 3)
#define R1_ERASE_SEQUENCE_ERROR (1 << 4)
#define R1_ADDRESS_ERROR (1 << 5)
#define R1_PARAMETER_ERROR (1 << 6)
// Types
#define SDCARD_NONE 0 /**< No card is present */
#define SDCARD_V1 1 /**< v1.x Standard Capacity */
#define SDCARD_V2 2 /**< v2.x Standard capacity SD card */
#define SDCARD_V2HC 3 /**< v2.x High capacity SD card */
#define CARD_UNKNOWN 4 /**< Unknown or unsupported card */
/* SIZE in Bytes */
#define PACKET_SIZE 6 /*!< SD Packet size CMD+ARG+CRC */
#define R1_RESPONSE_SIZE 1 /*!< Size of R1 response */
#define R2_RESPONSE_SIZE 2 /*!< Size of R2 response */
#define R3_R7_RESPONSE_SIZE 5 /*!< Size of R3/R7 response */
/* R1b Response */
#define DEVICE_BUSY (0x00)
/* R2 Response Format */
#define R2_CARD_LOCKED (1 << 0)
#define R2_CMD_FAILED (1 << 1)
#define R2_ERROR (1 << 2)
#define R2_CC_ERROR (1 << 3)
#define R2_CC_FAILED (1 << 4)
#define R2_WP_VIOLATION (1 << 5)
#define R2_ERASE_PARAM (1 << 6)
#define R2_OUT_OF_RANGE (1 << 7)
/* R3 Response : OCR Register */
#define OCR_HCS_CCS (0x1 << 30)
#define OCR_LOW_VOLTAGE (0x01 << 24)
#define OCR_3_3V (0x1 << 20)
/* R7 response pattern for CMD8 */
#define CMD8_PATTERN (0xAA)
/* CRC Enable */
#define CRC_ENABLE (0) /*!< CRC 1 - Enable 0 - Disable */
/* Control Tokens */
#define SPI_DATA_RESPONSE_MASK (0x1F)
#define SPI_DATA_ACCEPTED (0x05)
#define SPI_DATA_CRC_ERROR (0x0B)
#define SPI_DATA_WRITE_ERROR (0x0D)
#define SPI_START_BLOCK (0xFE) /*!< For Single Block Read/Write and Multiple Block Read */
#define SPI_START_BLK_MUL_WRITE (0xFC) /*!< Start Multi-block write */
#define SPI_STOP_TRAN (0xFD) /*!< Stop Multi-block write */
#define SPI_DATA_READ_ERROR_MASK (0xF) /*!< Data Error Token: 4 LSB bits */
#define SPI_READ_ERROR (0x1 << 0) /*!< Error */
#define SPI_READ_ERROR_CC (0x1 << 1) /*!< CC Error*/
#define SPI_READ_ERROR_ECC_C (0x1 << 2) /*!< Card ECC failed */
#define SPI_READ_ERROR_OFR (0x1 << 3) /*!< Out of Range */
SDBlockDevice::SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, uint64_t hz, bool crc_on)
: _sectors(0), _spi(mosi, miso, sclk), _cs(cs), _is_initialized(0),
_crc_on(crc_on), _init_ref_count(0), _crc16(0, 0, false, false)
{
_cs = 1;
_card_type = SDCARD_NONE;
// Set default to 100kHz for initialisation and 1MHz for data transfer
MBED_STATIC_ASSERT(((MBED_CONF_SD_INIT_FREQUENCY >= 100000) && (MBED_CONF_SD_INIT_FREQUENCY <= 400000)),
"Initialization frequency should be between 100KHz to 400KHz");
_init_sck = MBED_CONF_SD_INIT_FREQUENCY;
_transfer_sck = hz;
// Only HC block size is supported.
_block_size = BLOCK_SIZE_HC;
_erase_size = BLOCK_SIZE_HC;
}
SDBlockDevice::~SDBlockDevice()
{
if (_is_initialized) {
deinit();
}
}
int SDBlockDevice::_initialise_card()
{
// Detail debugging is for commands
_dbg = SD_DBG ? SD_CMD_TRACE : 0;
int32_t status = BD_ERROR_OK;
uint32_t response, arg;
// Initialize the SPI interface: Card by default is in SD mode
_spi_init();
// The card is transitioned from SDCard mode to SPI mode by sending the CMD0 + CS Asserted("0")
if (_go_idle_state() != R1_IDLE_STATE) {
debug_if(SD_DBG, "No disk, or could not put SD card in to SPI idle state\n");
return SD_BLOCK_DEVICE_ERROR_NO_DEVICE;
}
// Send CMD8, if the card rejects the command then it's probably using the
// legacy protocol, or is a MMC, or just flat-out broken
status = _cmd8();
if (BD_ERROR_OK != status && SD_BLOCK_DEVICE_ERROR_UNSUPPORTED != status) {
return status;
}
if (_crc_on) {
// Enable CRC
status = _cmd(CMD59_CRC_ON_OFF, _crc_on);
}
// Read OCR - CMD58 Response contains OCR register
if (BD_ERROR_OK != (status = _cmd(CMD58_READ_OCR, 0x0, 0x0, &response))) {
return status;
}
// Check if card supports voltage range: 3.3V
if (!(response & OCR_3_3V)) {
_card_type = CARD_UNKNOWN;
status = SD_BLOCK_DEVICE_ERROR_UNUSABLE;
return status;
}
// HCS is set 1 for HC/XC capacity cards for ACMD41, if supported
arg = 0x0;
if (SDCARD_V2 == _card_type) {
arg |= OCR_HCS_CCS;
}
/* Idle state bit in the R1 response of ACMD41 is used by the card to inform the host
* if initialization of ACMD41 is completed. "1" indicates that the card is still initializing.
* "0" indicates completion of initialization. The host repeatedly issues ACMD41 until
* this bit is set to "0".
*/
_spi_timer.start();
do {
status = _cmd(ACMD41_SD_SEND_OP_COND, arg, 1, &response);
} while ((response & R1_IDLE_STATE) && (_spi_timer.read_ms() < SD_COMMAND_TIMEOUT));
_spi_timer.stop();
// Initialization complete: ACMD41 successful
if ((BD_ERROR_OK != status) || (0x00 != response)) {
_card_type = CARD_UNKNOWN;
debug_if(SD_DBG, "Timeout waiting for card\n");
return status;
}
if (SDCARD_V2 == _card_type) {
// Get the card capacity CCS: CMD58
if (BD_ERROR_OK == (status = _cmd(CMD58_READ_OCR, 0x0, 0x0, &response))) {
// High Capacity card
if (response & OCR_HCS_CCS) {
_card_type = SDCARD_V2HC;
debug_if(SD_DBG, "Card Initialized: High Capacity Card \n");
} else {
debug_if(SD_DBG, "Card Initialized: Standard Capacity Card: Version 2.x \n");
}
}
} else {
_card_type = SDCARD_V1;
debug_if(SD_DBG, "Card Initialized: Version 1.x Card\n");
}
if (!_crc_on) {
// Disable CRC
status = _cmd(CMD59_CRC_ON_OFF, _crc_on);
}
return status;
}
int SDBlockDevice::init()
{
int err;
lock();
if (!_is_initialized) {
_init_ref_count = 0;
}
_init_ref_count++;
if (_init_ref_count != 1) {
goto end;
}
err = _initialise_card();
_is_initialized = (err == BD_ERROR_OK);
if (!_is_initialized) {
debug_if(SD_DBG, "Fail to initialize card\n");
unlock();
return err;
}
debug_if(SD_DBG, "init card = %d\n", _is_initialized);
_sectors = _sd_sectors();
// CMD9 failed
if (0 == _sectors) {
unlock();
return BD_ERROR_DEVICE_ERROR;
}
// Set block length to 512 (CMD16)
if (_cmd(CMD16_SET_BLOCKLEN, _block_size) != 0) {
debug_if(SD_DBG, "Set %d-byte block timed out\n", _block_size);
unlock();
return BD_ERROR_DEVICE_ERROR;
}
// Set SCK for data transfer
err = _freq();
if (err) {
unlock();
return err;
}
end:
unlock();
return BD_ERROR_OK;
}
int SDBlockDevice::deinit()
{
lock();
if (!_is_initialized) {
_init_ref_count = 0;
goto end;
}
_init_ref_count--;
if (_init_ref_count) {
goto end;
}
_is_initialized = false;
_sectors = 0;
end:
unlock();
return BD_ERROR_OK;
}
int SDBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
{
if (!is_valid_program(addr, size)) {
return SD_BLOCK_DEVICE_ERROR_PARAMETER;
}
lock();
if (!_is_initialized) {
unlock();
return SD_BLOCK_DEVICE_ERROR_NO_INIT;
}
const uint8_t *buffer = static_cast<const uint8_t *>(b);
int status = BD_ERROR_OK;
uint8_t response;
// Get block count
bd_addr_t blockCnt = size / _block_size;
// SDSC Card (CCS=0) uses byte unit address
// SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit)
if (SDCARD_V2HC == _card_type) {
addr = addr / _block_size;
}
// Send command to perform write operation
if (blockCnt == 1) {
// Single block write command
if (BD_ERROR_OK != (status = _cmd(CMD24_WRITE_BLOCK, addr))) {
unlock();
return status;
}
// Write data
response = _write(buffer, SPI_START_BLOCK, _block_size);
// Only CRC and general write error are communicated via response token
if (response != SPI_DATA_ACCEPTED) {
debug_if(SD_DBG, "Single Block Write failed: 0x%x \n", response);
status = SD_BLOCK_DEVICE_ERROR_WRITE;
}
} else {
// Pre-erase setting prior to multiple block write operation
_cmd(ACMD23_SET_WR_BLK_ERASE_COUNT, blockCnt, 1);
// Multiple block write command
if (BD_ERROR_OK != (status = _cmd(CMD25_WRITE_MULTIPLE_BLOCK, addr))) {
unlock();
return status;
}
// Write the data: one block at a time
do {
response = _write(buffer, SPI_START_BLK_MUL_WRITE, _block_size);
if (response != SPI_DATA_ACCEPTED) {
debug_if(SD_DBG, "Multiple Block Write failed: 0x%x \n", response);
break;
}
buffer += _block_size;
} while (--blockCnt); // Receive all blocks of data
/* In a Multiple Block write operation, the stop transmission will be done by
* sending 'Stop Tran' token instead of 'Start Block' token at the beginning
* of the next block
*/
_spi.write(SPI_STOP_TRAN);
}
_deselect();
unlock();
return status;
}
int SDBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
{
if (!is_valid_read(addr, size)) {
return SD_BLOCK_DEVICE_ERROR_PARAMETER;
}
lock();
if (!_is_initialized) {
unlock();
return SD_BLOCK_DEVICE_ERROR_PARAMETER;
}
uint8_t *buffer = static_cast<uint8_t *>(b);
int status = BD_ERROR_OK;
bd_addr_t blockCnt = size / _block_size;
// SDSC Card (CCS=0) uses byte unit address
// SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit)
if (SDCARD_V2HC == _card_type) {
addr = addr / _block_size;
}
// Write command ro receive data
if (blockCnt > 1) {
status = _cmd(CMD18_READ_MULTIPLE_BLOCK, addr);
} else {
status = _cmd(CMD17_READ_SINGLE_BLOCK, addr);
}
if (BD_ERROR_OK != status) {
unlock();
return status;
}
// receive the data : one block at a time
while (blockCnt) {
if (0 != _read(buffer, _block_size)) {
status = SD_BLOCK_DEVICE_ERROR_NO_RESPONSE;
break;
}
buffer += _block_size;
--blockCnt;
}
_deselect();
// Send CMD12(0x00000000) to stop the transmission for multi-block transfer
if (size > _block_size) {
status = _cmd(CMD12_STOP_TRANSMISSION, 0x0);
}
unlock();
return status;
}
bool SDBlockDevice::_is_valid_trim(bd_addr_t addr, bd_size_t size)
{
return (
addr % _erase_size == 0 &&
size % _erase_size == 0 &&
addr + size <= this->size());
}
int SDBlockDevice::trim(bd_addr_t addr, bd_size_t size)
{
if (!_is_valid_trim(addr, size)) {
return SD_BLOCK_DEVICE_ERROR_PARAMETER;
}
lock();
if (!_is_initialized) {
unlock();
return SD_BLOCK_DEVICE_ERROR_NO_INIT;
}
int status = BD_ERROR_OK;
size -= _block_size;
// SDSC Card (CCS=0) uses byte unit address
// SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit)
if (SDCARD_V2HC == _card_type) {
size = size / _block_size;
addr = addr / _block_size;
}
// Start lba sent in start command
if (BD_ERROR_OK != (status = _cmd(CMD32_ERASE_WR_BLK_START_ADDR, addr))) {
unlock();
return status;
}
// End lba = addr+size sent in end addr command
if (BD_ERROR_OK != (status = _cmd(CMD33_ERASE_WR_BLK_END_ADDR, addr + size))) {
unlock();
return status;
}
status = _cmd(CMD38_ERASE, 0x0);
unlock();
return status;
}
bd_size_t SDBlockDevice::get_read_size() const
{
return _block_size;
}
bd_size_t SDBlockDevice::get_program_size() const
{
return _block_size;
}
bd_size_t SDBlockDevice::size() const
{
return _block_size * _sectors;
}
const char *SDBlockDevice::get_type() const
{
return "SD";
}
void SDBlockDevice::debug(bool dbg)
{
_dbg = dbg;
}
int SDBlockDevice::frequency(uint64_t freq)
{
lock();
_transfer_sck = freq;
int err = _freq();
unlock();
return err;
}
// PRIVATE FUNCTIONS
int SDBlockDevice::_freq(void)
{
// Max frequency supported is 25MHZ
if (_transfer_sck <= 25000000) {
_spi.frequency(_transfer_sck);
return 0;
} else { // TODO: Switch function to be implemented for higher frequency
_transfer_sck = 25000000;
_spi.frequency(_transfer_sck);
return -EINVAL;
}
}
uint8_t SDBlockDevice::_cmd_spi(SDBlockDevice::cmdSupported cmd, uint32_t arg)
{
uint8_t response;
char cmdPacket[PACKET_SIZE];
uint32_t crc;
// Prepare the command packet
cmdPacket[0] = SPI_CMD(cmd);
cmdPacket[1] = (arg >> 24);
cmdPacket[2] = (arg >> 16);
cmdPacket[3] = (arg >> 8);
cmdPacket[4] = (arg >> 0);
if (_crc_on) {
_crc7.compute((void *)cmdPacket, 5, &crc);
cmdPacket[5] = (char)(crc | 0x01);
} else {
// CMD0 is executed in SD mode, hence should have correct CRC
// CMD8 CRC verification is always enabled
switch (cmd) {
case CMD0_GO_IDLE_STATE:
cmdPacket[5] = 0x95;
break;
case CMD8_SEND_IF_COND:
cmdPacket[5] = 0x87;
break;
default:
cmdPacket[5] = 0xFF; // Make sure bit 0-End bit is high
break;
}
}
// send a command
for (int i = 0; i < PACKET_SIZE; i++) {
_spi.write(cmdPacket[i]);
}
// The received byte immediataly following CMD12 is a stuff byte,
// it should be discarded before receive the response of the CMD12.
if (CMD12_STOP_TRANSMISSION == cmd) {
_spi.write(SPI_FILL_CHAR);
}
// Loop for response: Response is sent back within command response time (NCR), 0 to 8 bytes for SDC
for (int i = 0; i < 0x10; i++) {
response = _spi.write(SPI_FILL_CHAR);
// Got the response
if (!(response & R1_RESPONSE_RECV)) {
break;
}
}
return response;
}
int SDBlockDevice::_cmd(SDBlockDevice::cmdSupported cmd, uint32_t arg, bool isAcmd, uint32_t *resp)
{
int32_t status = BD_ERROR_OK;
uint32_t response;
// Select card and wait for card to be ready before sending next command
// Note: next command will fail if card is not ready
_select();
// No need to wait for card to be ready when sending the stop command
if (CMD12_STOP_TRANSMISSION != cmd) {
if (false == _wait_ready(SD_COMMAND_TIMEOUT)) {
debug_if(SD_DBG, "Card not ready yet \n");
}
}
// Re-try command
for (int i = 0; i < 3; i++) {
// Send CMD55 for APP command first
if (isAcmd) {
response = _cmd_spi(CMD55_APP_CMD, 0x0);
// Wait for card to be ready after CMD55
if (false == _wait_ready(SD_COMMAND_TIMEOUT)) {
debug_if(SD_DBG, "Card not ready yet \n");
}
}
// Send command over SPI interface
response = _cmd_spi(cmd, arg);
if (R1_NO_RESPONSE == response) {
debug_if(SD_DBG, "No response CMD:%d \n", cmd);
continue;
}
break;
}
// Pass the response to the command call if required
if (NULL != resp) {
*resp = response;
}
// Process the response R1 : Exit on CRC/Illegal command error/No response
if (R1_NO_RESPONSE == response) {
_deselect();
debug_if(SD_DBG, "No response CMD:%d response: 0x%x\n", cmd, response);
return SD_BLOCK_DEVICE_ERROR_NO_DEVICE; // No device
}
if (response & R1_COM_CRC_ERROR) {
_deselect();
debug_if(SD_DBG, "CRC error CMD:%d response 0x%x \n", cmd, response);
return SD_BLOCK_DEVICE_ERROR_CRC; // CRC error
}
if (response & R1_ILLEGAL_COMMAND) {
_deselect();
debug_if(SD_DBG, "Illegal command CMD:%d response 0x%x\n", cmd, response);
if (CMD8_SEND_IF_COND == cmd) { // Illegal command is for Ver1 or not SD Card
_card_type = CARD_UNKNOWN;
}
return SD_BLOCK_DEVICE_ERROR_UNSUPPORTED; // Command not supported
}
debug_if(_dbg, "CMD:%d \t arg:0x%x \t Response:0x%x \n", cmd, arg, response);
// Set status for other errors
if ((response & R1_ERASE_RESET) || (response & R1_ERASE_SEQUENCE_ERROR)) {
status = SD_BLOCK_DEVICE_ERROR_ERASE; // Erase error
} else if ((response & R1_ADDRESS_ERROR) || (response & R1_PARAMETER_ERROR)) {
// Misaligned address / invalid address block length
status = SD_BLOCK_DEVICE_ERROR_PARAMETER;
}
// Get rest of the response part for other commands
switch (cmd) {
case CMD8_SEND_IF_COND: // Response R7
debug_if(_dbg, "V2-Version Card\n");
_card_type = SDCARD_V2;
// Note: No break here, need to read rest of the response
case CMD58_READ_OCR: // Response R3
response = (_spi.write(SPI_FILL_CHAR) << 24);
response |= (_spi.write(SPI_FILL_CHAR) << 16);
response |= (_spi.write(SPI_FILL_CHAR) << 8);
response |= _spi.write(SPI_FILL_CHAR);
debug_if(_dbg, "R3/R7: 0x%x \n", response);
break;
case CMD12_STOP_TRANSMISSION: // Response R1b
case CMD38_ERASE:
_wait_ready(SD_COMMAND_TIMEOUT);
break;
case ACMD13_SD_STATUS: // Response R2
response = _spi.write(SPI_FILL_CHAR);
debug_if(_dbg, "R2: 0x%x \n", response);
break;
default: // Response R1
break;
}
// Pass the updated response to the command
if (NULL != resp) {
*resp = response;
}
// Do not deselect card if read is in progress.
if (((CMD9_SEND_CSD == cmd) || (ACMD22_SEND_NUM_WR_BLOCKS == cmd) ||
(CMD24_WRITE_BLOCK == cmd) || (CMD25_WRITE_MULTIPLE_BLOCK == cmd) ||
(CMD17_READ_SINGLE_BLOCK == cmd) || (CMD18_READ_MULTIPLE_BLOCK == cmd))
&& (BD_ERROR_OK == status)) {
return BD_ERROR_OK;
}
// Deselect card
_deselect();
return status;
}
int SDBlockDevice::_cmd8()
{
uint32_t arg = (CMD8_PATTERN << 0); // [7:0]check pattern
uint32_t response = 0;
int32_t status = BD_ERROR_OK;
arg |= (0x1 << 8); // 2.7-3.6V // [11:8]supply voltage(VHS)
status = _cmd(CMD8_SEND_IF_COND, arg, 0x0, &response);
// Verify voltage and pattern for V2 version of card
if ((BD_ERROR_OK == status) && (SDCARD_V2 == _card_type)) {
// If check pattern is not matched, CMD8 communication is not valid
if ((response & 0xFFF) != arg) {
debug_if(SD_DBG, "CMD8 Pattern mismatch 0x%x : 0x%x\n", arg, response);
_card_type = CARD_UNKNOWN;
status = SD_BLOCK_DEVICE_ERROR_UNUSABLE;
}
}
return status;
}
uint32_t SDBlockDevice::_go_idle_state()
{
uint32_t response;
/* Reseting the MCU SPI master may not reset the on-board SDCard, in which
* case when MCU power-on occurs the SDCard will resume operations as
* though there was no reset. In this scenario the first CMD0 will
* not be interpreted as a command and get lost. For some cards retrying
* the command overcomes this situation. */
for (int i = 0; i < SD_CMD0_GO_IDLE_STATE_RETRIES; i++) {
_cmd(CMD0_GO_IDLE_STATE, 0x0, 0x0, &response);
if (R1_IDLE_STATE == response) {
break;
}
wait_ms(1);
}
return response;
}
int SDBlockDevice::_read_bytes(uint8_t *buffer, uint32_t length)
{
uint16_t crc;
// read until start byte (0xFE)
if (false == _wait_token(SPI_START_BLOCK)) {
debug_if(SD_DBG, "Read timeout\n");
_deselect();
return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE;
}
// read data
for (uint32_t i = 0; i < length; i++) {
buffer[i] = _spi.write(SPI_FILL_CHAR);
}
// Read the CRC16 checksum for the data block
crc = (_spi.write(SPI_FILL_CHAR) << 8);
crc |= _spi.write(SPI_FILL_CHAR);
if (_crc_on) {
uint32_t crc_result;
// Compute and verify checksum
_crc16.compute((void *)buffer, length, &crc_result);
if ((uint16_t)crc_result != crc) {
debug_if(SD_DBG, "_read_bytes: Invalid CRC received 0x%x result of computation 0x%x\n",
crc, crc_result);
_deselect();
return SD_BLOCK_DEVICE_ERROR_CRC;
}
}
_deselect();
return 0;
}
int SDBlockDevice::_read(uint8_t *buffer, uint32_t length)
{
uint16_t crc;
// read until start byte (0xFE)
if (false == _wait_token(SPI_START_BLOCK)) {
debug_if(SD_DBG, "Read timeout\n");
_deselect();
return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE;
}
// read data
_spi.write(NULL, 0, (char *)buffer, length);
// Read the CRC16 checksum for the data block
crc = (_spi.write(SPI_FILL_CHAR) << 8);
crc |= _spi.write(SPI_FILL_CHAR);
if (_crc_on) {
uint32_t crc_result;
// Compute and verify checksum
_crc16.compute((void *)buffer, length, &crc_result);
if ((uint16_t)crc_result != crc) {
debug_if(SD_DBG, "_read_bytes: Invalid CRC received 0x%x result of computation 0x%x\n",
crc, crc_result);
return SD_BLOCK_DEVICE_ERROR_CRC;
}
}
return 0;
}
uint8_t SDBlockDevice::_write(const uint8_t *buffer, uint8_t token, uint32_t length)
{
uint32_t crc = (~0);
uint8_t response = 0xFF;
// indicate start of block
_spi.write(token);
// write the data
_spi.write((char *)buffer, length, NULL, 0);
if (_crc_on) {
// Compute CRC
_crc16.compute((void *)buffer, length, &crc);
}
// write the checksum CRC16
_spi.write(crc >> 8);
_spi.write(crc);
// check the response token
response = _spi.write(SPI_FILL_CHAR);
// Wait for last block to be written
if (false == _wait_ready(SD_COMMAND_TIMEOUT)) {
debug_if(SD_DBG, "Card not ready yet \n");
}
return (response & SPI_DATA_RESPONSE_MASK);
}
static uint32_t ext_bits(unsigned char *data, int msb, int lsb)
{
uint32_t bits = 0;
uint32_t size = 1 + msb - lsb;
for (uint32_t i = 0; i < size; i++) {
uint32_t position = lsb + i;
uint32_t byte = 15 - (position >> 3);
uint32_t bit = position & 0x7;
uint32_t value = (data[byte] >> bit) & 1;
bits |= value << i;
}
return bits;
}
bd_size_t SDBlockDevice::_sd_sectors()
{
uint32_t c_size, c_size_mult, read_bl_len;
uint32_t block_len, mult, blocknr;
uint32_t hc_c_size;
bd_size_t blocks = 0, capacity = 0;
// CMD9, Response R2 (R1 byte + 16-byte block read)
if (_cmd(CMD9_SEND_CSD, 0x0) != 0x0) {
debug_if(SD_DBG, "Didn't get a response from the disk\n");
return 0;
}
uint8_t csd[16];
if (_read_bytes(csd, 16) != 0) {
debug_if(SD_DBG, "Couldn't read csd response from disk\n");