-
Notifications
You must be signed in to change notification settings - Fork 8
/
SD.c
executable file
·452 lines (296 loc) · 9.27 KB
/
SD.c
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
//(c) uARM project https://github.com/uARM-Palm/uARM uARM@dmitry.gr
#include "SD.h"
#include <avr/io.h>
#define FLAG_TIMEOUT 0x80
#define FLAG_PARAM_ERR 0x40
#define FLAG_ADDR_ERR 0x20
#define FLAG_ERZ_SEQ_ERR 0x10
#define FLAG_CMD_CRC_ERR 0x08
#define FLAG_ILLEGAL_CMD 0x04
#define FLAG_ERZ_RST 0x02
#define FLAG_IN_IDLE_MODE 0x01
#define SD_PIN_MOSI ((uint8_t)(1 << 6))
#define SD_PIN_MISO ((uint8_t)(1 << 6))
#define SD_PIN_SCLK ((uint8_t)(1 << 7))
bool gSdSpiFast = true;
static void sdSpiDelay(){
volatile uint16_t t = 0;
t++;
t--;
}
static void sdClockSpeed(bool fast){
gSdSpiFast = fast;
}
uint8_t sdSpiByte(uint8_t v);
uint8_t sdSpiByteSlow(uint8_t v){
uint8_t i;
for(i = 0; i < 8; i++){
if(v & 0x80) PORTB |= SD_PIN_MOSI;
else PORTB &=~ SD_PIN_MOSI;
sdSpiDelay();
PORTB |= SD_PIN_SCLK;
sdSpiDelay();
v <<= 1;
if(PIND & SD_PIN_MISO) v++;
PORTB &=~ SD_PIN_SCLK;
sdSpiDelay();
}
PORTB &=~ SD_PIN_MOSI;
return v;
}
static void sdSpiSingleClock(void){
sdSpiDelay();
PORTB |= SD_PIN_MOSI;
sdSpiDelay();
PORTB |= SD_PIN_SCLK;
sdSpiDelay();
PORTB &=~ SD_PIN_SCLK;
sdSpiDelay();
}
uint8_t sdCrc7(uint8_t* chr,uint8_t cnt,uint8_t crc){
uint8_t i, a;
uint8_t Data;
for(a = 0; a < cnt; a++){
Data = chr[a];
for(i = 0; i < 8; i++){
crc <<= 1;
if( (Data & 0x80) ^ (crc & 0x80) ) crc ^= 0x09;
Data <<= 1;
}
}
return crc & 0x7F;
}
static inline void sdPrvSendCmd(uint8_t cmd, uint32_t param, bool crc){
uint8_t send[6];
uint8_t L = crc ? 6 : 5;
send[0] = cmd | 0x40;
send[1] = param >> 24;
send[2] = param >> 16;
send[3] = param >> 8;
send[4] = param;
send[5] = crc ? (sdCrc7(send, 5, 0) << 1) | 1 : 0;
for(cmd = 0; cmd < L; cmd++) sdSpiByte(send[cmd]);
}
static uint8_t sdPrvSimpleCommand(uint8_t cmd, uint32_t param, bool crc){ //do a command, return R1 reply
uint8_t ret;
uint8_t i = 0;
sdPrvSendCmd(cmd, param, crc);
do{ //our max wait time is 128 byte clocks (1024 clock ticks)
ret = sdSpiByte(0xFF);
}while(i++ < 128 && (ret == 0xFF));
return ret;
}
static uint8_t sdPrvReadData(uint8_t* data, uint16_t sz){
uint8_t ret;
uint8_t tries = 200;
do{
ret = sdSpiByte(0xFF);
if((ret & 0xF0) == 0x00) return ret; //fail
if(ret == 0xFE) break;
tries--;
}while(tries);
if(!tries) return 0xFF;
*data = ret;
ret = 0;
while(sz--) *data++ = sdSpiByte(0xFF);
return ret;
}
static uint8_t sdPrvACMD(uint8_t cmd, uint32_t param, bool crc){
uint8_t ret;
ret = sdPrvSimpleCommand(55, 0, crc);
if(ret & FLAG_TIMEOUT) return ret;
if(ret & FLAG_ILLEGAL_CMD) return ret;
return sdPrvSimpleCommand(cmd, param, crc);
}
bool sdPrvCardInit(bool sd, bool hc){
uint32_t time = 0;
uint32_t resp;
bool first = true;
uint32_t param;
#ifdef NO_HC_CARDS
sendHCS = 0;
#endif
param = hc ? (1ULL<< 30) : 0;
while(time++ < 10000UL){ //retry 10..0 times
//busy bit is top bit of OCR, which is top bit of resp[1]
//busy bit at 1 means init complete
//see pages 85 and 26
resp = sd ? sdPrvACMD(41, param, true) : sdPrvSimpleCommand(1, param, true);
if(resp & FLAG_TIMEOUT) break;
if(first){
param = (hc ? (1UL << 30) : 0UL) | 0x00200000UL;
first = false;
}
else{
if(!(resp & FLAG_IN_IDLE_MODE)) return true;
}
}
return false;
}
uint32_t sdPrvGetBits(uint8_t* data,uint32_t numBytesInArray,uint32_t startBit,uint32_t len){//for CID and CSD data..
uint32_t bitWrite = 0;
uint32_t numBitsInArray = numBytesInArray * 8;
uint32_t ret = 0;
do{
uint32_t bit,byte;
bit = numBitsInArray - startBit - 1;
byte = bit / 8;
bit = 7 - (bit % 8);
ret |= ((data[byte] >> bit) & 1) << (bitWrite++);
startBit++;
}while(--len);
return ret;
}
static uint32_t sdPrvGetCardNumBlocks(bool mmc,uint8_t* csd){
uint32_t ver = sdPrvGetBits(csd,16,126,2);
uint32_t cardSz = 0;
if(ver == 0 || (mmc && ver <= 2)){
uint32_t cSize = sdPrvGetBits(csd,16,62,12);
uint32_t cSizeMult = sdPrvGetBits(csd,16,47,3);
uint32_t readBlLen = sdPrvGetBits(csd,16,80,4);
uint32_t blockLen,blockNr;
uint32_t divTimes = 9; //from bytes to blocks division
blockLen = 1UL << readBlLen;
blockNr = (cSize + 1) * (1UL << (cSizeMult + 2));
/*
multiplying those two produces result in bytes, we need it in blocks
so we shift right 9 times. doing it after multiplication might fuck up
the 4GB card, so we do it before, but to avoid killing significant bits
we only cut the zero-valued bits, if at the end we end up with non-zero
"divTimes", divide after multiplication, and thus underuse the card a bit.
This will never happen in reality since 512 is 2^9, and we are
multiplying two numbers whose product is a multiple of 2^9, so they
togethr should have at least 9 lower zero bits.
*/
while(divTimes && !(blockLen & 1)){
blockLen = blockLen >> 1;
divTimes--;
}
while(divTimes && !(blockNr & 1)){
blockNr = blockNr >> 1;
divTimes--;
}
cardSz = (blockNr * blockLen) >> divTimes;
}
else if(ver == 1){
cardSz = sdPrvGetBits(csd,16,48,22)/*num 512K blocks*/ << 10;
}
return cardSz;
}
bool sdInit(SD* sd){
#ifdef SIM
sd->numSec = *(volatile unsigned char*)0xD4;
sd->numSec <<= 8;
sd->numSec |= *(volatile unsigned char*)0xD5;
sd->numSec <<= 8;
sd->numSec |= *(volatile unsigned char*)0xD6;
sd->inited = 1;
#else
uint8_t v;
uint16_t tries = 0; //needs to go high, in case we were in middle of block read before...
uint8_t respBuf[16];
sd->inited = false;
sd->SD = false;
sdClockSpeed(false);
for(v = 0; v < 10; v++) sdSpiByte(0xFF); //80 clocks with CS not asserted to give card time to init
//with CS tied low, we get here with clock sync a bit weird, so we need to re-sync it, we do so here, since we know for sure what the valid RESP for CMD0 is
do{
sdSpiSingleClock();
v = sdPrvSimpleCommand(0, 0, true);
//resync usage makes this bad, so i comment it out: if(v & FLAG_TIMEOUT) return false;
tries++;
if(tries > 600L) return false;
}while(v != 0x01);
v = sdPrvSimpleCommand(8, 0x000001AAUL, true); //try CMD8 to init SDHC cards
if(v & FLAG_TIMEOUT) return false;
sd->HC = !(v & FLAG_ILLEGAL_CMD);
v = sdPrvSimpleCommand(55, 0, true); //see if this is SD or MMC
if(v & FLAG_TIMEOUT) return false;
sd->SD = !(v & FLAG_ILLEGAL_CMD);
if(sd->SD){
if(!sdPrvCardInit(true, true) && !sdPrvCardInit(true, false)){
return false;
}
}
else{
if(!sdPrvCardInit(false, true) && !sdPrvCardInit(false, false)){
return false;
}
}
v = sdPrvSimpleCommand(59, 0, true); //crc off
if(v & FLAG_TIMEOUT) return false;
v = sdPrvSimpleCommand(9, 0, false); //read CSD
if(v & FLAG_TIMEOUT) return false;
v = sdPrvReadData(respBuf, 16);
if(v) return false;
sd->numSec = sdPrvGetCardNumBlocks(!sd->SD, respBuf);
sd->inited = true;
sdClockSpeed(true);
#endif
return true;
}
uint32_t sdGetNumSec(SD* sd){
return sd->inited ? sd->numSec : 0;
}
void writechar(int chr);
bool sdSecRead(SD* sd, uint32_t sec, void* buf){ //CMD17
#ifdef SIM
uint16_t t;
uint8_t* b = buf;
*(volatile unsigned char*)0xD4 = sec >> 16;
*(volatile unsigned char*)0xD5 = sec >> 8;
*(volatile unsigned char*)0xD6 = sec;
for(t = 0; t < 512; t++) *b++ = *(volatile unsigned char*)0xD7;
return true;
#else
uint8_t v, retry = 0;
bool ret = false;
PIND = (uint8_t)(1 << 2); //LED_r
if(!sd->inited) return false;
do{
v = sdPrvSimpleCommand(17, sd->HC ? sec : sec << 9, false);
if(v & FLAG_TIMEOUT) return false;
v = sdPrvReadData(buf, SD_BLOCK_SIZE);
if(!v){
ret = true;
break;
}
}while(++retry < 5); //retry up to 5 times
PIND = (uint8_t)(1 << 2); //LED_r
return ret;
#endif
}
bool sdSecWrite(SD* sd, uint32_t sec, void* buf){ //CMD24
#ifdef SIM
uint16_t t;
uint8_t* b = buf;
*(unsigned char*)0xD4 = sec >> 16;
*(unsigned char*)0xD5 = sec >> 8;
*(unsigned char*)0xD6 = sec;
for(t = 0; t < 512; t++) *(unsigned char*)0xD7 = *b++;
return true;
#else
uint16_t v16;
uint8_t v, retry = 0;
uint8_t* buf_ = buf;
bool ret = false;
//writechar('W');
PIND = (uint8_t)(1 << 3); //LED_w
if(!sd->inited) return false;
do{
v = sdPrvSimpleCommand(24, sd->HC ? sec : sec << 9, false);
if(v & FLAG_TIMEOUT) return false;
sdSpiByte(0xFF); //as per SD-spi spec, we give it 8 clocks to consider the ramifications of the command we just sent
sdSpiByte(0xFE); //start of data block
for(v16 = 0; v16 < SD_BLOCK_SIZE; v16++) sdSpiByte(*buf_++); //data
while((v = sdSpiByte(0xFF)) == 0xFF); //wait while card isnt answering
while(sdSpiByte(0xFF) != 0xFF); //wait while card is busy
if((v & 0x1F) == 5){
ret = true;
break;
}
}while(++retry < 5); //retry up to 5 times
PIND = (uint8_t)(1 << 3); //LED_w
return ret;
#endif
}