Fix bug in using nor flash storage without SD
[SCSI2SD.git] / software / SCSI2SD / src / sd.c
1 //      Copyright (C) 2013 Michael McMaster <michael@codesrc.com>\r
2 //\r
3 //      This file is part of SCSI2SD.\r
4 //\r
5 //      SCSI2SD is free software: you can redistribute it and/or modify\r
6 //      it under the terms of the GNU General Public License as published by\r
7 //      the Free Software Foundation, either version 3 of the License, or\r
8 //      (at your option) any later version.\r
9 //\r
10 //      SCSI2SD is distributed in the hope that it will be useful,\r
11 //      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 //      GNU General Public License for more details.\r
14 //\r
15 //      You should have received a copy of the GNU General Public License\r
16 //      along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 #include "device.h"\r
19 #include "scsi.h"\r
20 #include "config.h"\r
21 #include "disk.h"\r
22 #include "sd.h"\r
23 #include "led.h"\r
24 #include "time.h"\r
25 #include "trace.h"\r
26 \r
27 #include "scsiPhy.h"\r
28 \r
29 #include <string.h>\r
30 \r
31 static void sd_earlyInit(S2S_Device* dev);\r
32 static void sd_deviceInit(S2S_Device* dev);\r
33 static S2S_Target* sd_getTargets(S2S_Device* dev, int* count);\r
34 static uint32_t sd_getCapacity(S2S_Device* dev);\r
35 static int sd_pollMediaChange(S2S_Device* dev);\r
36 static void sd_pollMediaBusy(S2S_Device* dev);\r
37 static void sd_erase(S2S_Device* dev, uint32_t sectorNumber, uint32_t count);\r
38 static void sd_read(S2S_Device* dev, uint32_t sectorNumber, uint32_t count, uint8_t* buffer);\r
39 static void sd_readAsync(S2S_Device* dev, uint32_t sectorNumber, uint32_t count, uint8_t* buffer);\r
40 static int  sd_readAsyncPoll(S2S_Device* dev);\r
41 static void sd_write(S2S_Device* dev, uint32_t sectorNumber, uint32_t count, const uint8_t* buffer);\r
42 \r
43 \r
44 // Global\r
45 SdCard sdCard = {\r
46         {\r
47                 sd_earlyInit,\r
48         sd_deviceInit,\r
49                 sd_getTargets,\r
50                 sd_getCapacity,\r
51                 sd_pollMediaChange,\r
52                 sd_pollMediaBusy,\r
53         sd_erase,\r
54         sd_read,\r
55         sd_readAsync,\r
56         sd_readAsyncPoll,\r
57         sd_write,\r
58         0, // initial mediaState\r
59         CONFIG_STOREDEVICE_SD\r
60         }\r
61 };\r
62 S2S_Device* sdDevice = &(sdCard.dev);\r
63 \r
64 enum SD_CMD_STATE { CMD_STATE_IDLE, CMD_STATE_READ, CMD_STATE_WRITE };\r
65 static int sdCmdState = CMD_STATE_IDLE;\r
66 static uint32_t sdCmdNextLBA; // Only valid in CMD_STATE_READ or CMD_STATE_WRITE\r
67 static uint32_t sdCmdTime;\r
68 static uint32_t sdLastCmdState = CMD_STATE_IDLE;\r
69 \r
70 enum SD_IO_STATE { SD_DMA, SD_ACCEPTED, SD_IDLE };\r
71 static int sdIOState = SD_IDLE;\r
72 \r
73 // Private DMA variables.\r
74 static uint8 sdDMARxChan = CY_DMA_INVALID_CHANNEL;\r
75 static uint8 sdDMATxChan = CY_DMA_INVALID_CHANNEL;\r
76 \r
77 // Dummy location for DMA to send unchecked CRC bytes to\r
78 static uint8 discardBuffer __attribute__((aligned(4)));\r
79 \r
80 // 2 bytes CRC, response, 8bits to close the clock..\r
81 // "NCR" time is up to 8 bytes.\r
82 static uint8_t writeResponseBuffer[8]  __attribute__((aligned(4)));\r
83 \r
84 // Padded with a dummy byte just to allow the tx DMA channel to\r
85 // use 2-byte bursts for performance.\r
86 static uint8_t writeStartToken[2]  __attribute__((aligned(4))) = {0xFF, 0xFC};\r
87 \r
88 // Source of dummy SPI bytes for DMA\r
89 static uint8_t dummyBuffer[2]  __attribute__((aligned(4))) = {0xFF, 0xFF};\r
90 \r
91 volatile uint8_t sdRxDMAComplete;\r
92 volatile uint8_t sdTxDMAComplete;\r
93 \r
94 static void sdCompleteRead();\r
95 static void sdCompleteWrite();\r
96 \r
97 CY_ISR_PROTO(sdRxISR);\r
98 CY_ISR(sdRxISR)\r
99 {\r
100         sdRxDMAComplete = 1;\r
101 }\r
102 CY_ISR_PROTO(sdTxISR);\r
103 CY_ISR(sdTxISR)\r
104 {\r
105         sdTxDMAComplete = 1;\r
106 }\r
107 \r
108 static uint8 sdCrc7(uint8* chr, uint8 cnt, uint8 crc)\r
109 {\r
110         uint8 a;\r
111         for(a = 0; a < cnt; a++)\r
112         {\r
113                 uint8 Data = chr[a];\r
114                 uint8 i;\r
115                 for(i = 0; i < 8; i++)\r
116                 {\r
117                         crc <<= 1;\r
118                         if( (Data & 0x80) ^ (crc & 0x80) ) {crc ^= 0x09;}\r
119                         Data <<= 1;\r
120                 }\r
121         }\r
122         return crc & 0x7F;\r
123 }\r
124 \r
125 \r
126 // Read and write 1 byte.\r
127 static uint8_t sdSpiByte(uint8_t value)\r
128 {\r
129         SDCard_WriteTxData(value);\r
130         trace(trace_spinSpiByte);\r
131         while (!(SDCard_ReadRxStatus() & SDCard_STS_RX_FIFO_NOT_EMPTY)) {}\r
132         trace(trace_sdSpiByte);\r
133         return SDCard_ReadRxData();\r
134 }\r
135 \r
136 static void sdWaitWriteBusy()\r
137 {\r
138         uint8 val;\r
139         do\r
140         {\r
141                 val = sdSpiByte(0xFF);\r
142         } while (val != 0xFF);\r
143 }\r
144 \r
145 static void sdPreCmdState(uint32_t newState)\r
146 {\r
147         if (sdCmdState == CMD_STATE_READ)\r
148         {\r
149                 sdCompleteRead();\r
150         }\r
151         else if (sdCmdState == CMD_STATE_WRITE)\r
152         {\r
153                 sdCompleteWrite();\r
154         }\r
155         sdCmdState = CMD_STATE_IDLE;\r
156 \r
157         if (sdLastCmdState != newState && newState != CMD_STATE_IDLE)\r
158         {\r
159                 sdWaitWriteBusy();\r
160                 sdLastCmdState = newState;\r
161         }\r
162 }\r
163 \r
164 static uint16_t sdDoCommand(\r
165         uint8_t cmd,\r
166         uint32_t param,\r
167         int useCRC,\r
168         int use2byteResponse)\r
169 {\r
170         int waitWhileBusy = (cmd != SD_GO_IDLE_STATE) && (cmd != SD_STOP_TRANSMISSION);\r
171 \r
172         // "busy" probe. We'll examine the results later.\r
173         if (waitWhileBusy)\r
174         {\r
175                 SDCard_WriteTxData(0xFF);\r
176         }\r
177 \r
178         // send is static as the address must remain consistent for the static\r
179         // DMA descriptors to work.\r
180         // Size must be divisible by 2 to suit 2-byte-burst TX DMA channel.\r
181         static uint8_t send[6] __attribute__((aligned(4)));\r
182         send[0] = cmd | 0x40;\r
183         send[1] = param >> 24;\r
184         send[2] = param >> 16;\r
185         send[3] = param >> 8;\r
186         send[4] = param;\r
187         if (unlikely(useCRC))\r
188         {\r
189                 send[5] = (sdCrc7(send, 5, 0) << 1) | 1;\r
190         }\r
191         else\r
192         {\r
193                 send[5] = 1; // stop bit\r
194         }\r
195 \r
196         static uint8_t dmaRxTd = CY_DMA_INVALID_TD;\r
197         static uint8_t dmaTxTd = CY_DMA_INVALID_TD;\r
198         if (unlikely(dmaRxTd == CY_DMA_INVALID_TD))\r
199         {\r
200                 dmaRxTd = CyDmaTdAllocate();\r
201                 dmaTxTd = CyDmaTdAllocate();\r
202                 CyDmaTdSetConfiguration(dmaTxTd, sizeof(send), CY_DMA_DISABLE_TD, TD_INC_SRC_ADR|SD_TX_DMA__TD_TERMOUT_EN);\r
203                 CyDmaTdSetAddress(dmaTxTd, LO16((uint32)&send), LO16((uint32)SDCard_TXDATA_PTR));\r
204                 CyDmaTdSetConfiguration(dmaRxTd, sizeof(send), CY_DMA_DISABLE_TD, SD_RX_DMA__TD_TERMOUT_EN);\r
205                 CyDmaTdSetAddress(dmaRxTd, LO16((uint32)SDCard_RXDATA_PTR), LO16((uint32)&discardBuffer));\r
206         }\r
207 \r
208         sdTxDMAComplete = 0;\r
209         sdRxDMAComplete = 0;\r
210 \r
211         CyDmaChSetInitialTd(sdDMARxChan, dmaRxTd);\r
212         CyDmaChSetInitialTd(sdDMATxChan, dmaTxTd);\r
213 \r
214         // Some Samsung cards enter a busy-state after single-sector reads.\r
215         // But we also need to wait for R1B to complete from the multi-sector\r
216         // reads.\r
217         if (waitWhileBusy)\r
218         {\r
219                 trace(trace_spinSDRxFIFO);\r
220                 while (!(SDCard_ReadRxStatus() & SDCard_STS_RX_FIFO_NOT_EMPTY)) {}\r
221                 int busy = SDCard_ReadRxData() != 0xFF;\r
222                 if (unlikely(busy))\r
223                 {\r
224                         trace(trace_spinSDBusy);\r
225                         while (sdSpiByte(0xFF) != 0xFF) {}\r
226                 }\r
227         }\r
228 \r
229         // The DMA controller is a bit trigger-happy. It will retain\r
230         // a drq request that was triggered while the channel was\r
231         // disabled.\r
232         CyDmaChSetRequest(sdDMATxChan, CY_DMA_CPU_REQ);\r
233         CyDmaClearPendingDrq(sdDMARxChan);\r
234 \r
235         // There is no flow control, so we must ensure we can read the bytes\r
236         // before we start transmitting\r
237         CyDmaChEnable(sdDMARxChan, 1);\r
238         CyDmaChEnable(sdDMATxChan, 1);\r
239 \r
240         trace(trace_spinSDDMA);\r
241         int allComplete = 0;\r
242         while (!allComplete)\r
243         {\r
244                 uint8_t intr = CyEnterCriticalSection();\r
245                 allComplete = sdTxDMAComplete && sdRxDMAComplete;\r
246                 if (!allComplete)\r
247                 {\r
248                         __WFI();\r
249                 }\r
250                 CyExitCriticalSection(intr);\r
251         }\r
252 \r
253         uint16_t response = sdSpiByte(0xFF); // Result code or stuff byte\r
254         if (unlikely(cmd == SD_STOP_TRANSMISSION))\r
255         {\r
256                 // Stuff byte is required for this command only.\r
257                 // Part 1 Simplified standard 3.01\r
258                 // "The stop command has an execution delay due to the serial command\r
259                 // transmission."\r
260                 response = sdSpiByte(0xFF);\r
261         }\r
262 \r
263         uint32_t start = getTime_ms();\r
264 \r
265         trace(trace_spinSDBusy);\r
266         while ((response & 0x80) && likely(elapsedTime_ms(start) <= 200))\r
267         {\r
268                 response = sdSpiByte(0xFF);\r
269         }\r
270         if (unlikely(use2byteResponse))\r
271         {\r
272                 response = (response << 8) | sdSpiByte(0xFF);\r
273         }\r
274         return response;\r
275 }\r
276 \r
277 \r
278 static inline uint16_t sdCommandAndResponse(uint8_t cmd, uint32_t param)\r
279 {\r
280         return sdDoCommand(cmd, param, 0, 0);\r
281 }\r
282 \r
283 static inline uint16_t sdCRCCommandAndResponse(uint8_t cmd, uint32_t param)\r
284 {\r
285         return sdDoCommand(cmd, param, 1, 0);\r
286 }\r
287 \r
288 // Clear the sticky status bits on error.\r
289 static void sdClearStatus()\r
290 {\r
291         sdSpiByte(0xFF);\r
292         uint16_t r2 = sdDoCommand(SD_SEND_STATUS, 0, 1, 1);\r
293         (void) r2;\r
294 }\r
295 \r
296 void\r
297 sdReadMultiSectorPrep(uint32_t sdLBA, uint32_t sdSectors)\r
298 {\r
299         uint32_t tmpNextLBA = sdLBA + sdSectors;\r
300 \r
301         if (!sdCard.ccs)\r
302         {\r
303                 sdLBA = sdLBA * SD_SECTOR_SIZE;\r
304                 tmpNextLBA = tmpNextLBA * SD_SECTOR_SIZE;\r
305         }\r
306 \r
307         if (sdCmdState == CMD_STATE_READ && sdCmdNextLBA == sdLBA)\r
308         {\r
309                 // Well, that was lucky. We're already reading this data\r
310                 sdCmdNextLBA = tmpNextLBA;\r
311                 sdCmdTime = getTime_ms();\r
312         }\r
313         else\r
314         {\r
315                 sdPreCmdState(CMD_STATE_READ);\r
316 \r
317                 uint8_t v = sdCommandAndResponse(SD_READ_MULTIPLE_BLOCK, sdLBA);\r
318                 if (unlikely(v))\r
319                 {\r
320                         scsiDiskReset();\r
321                         sdClearStatus();\r
322 \r
323                         scsiDev.status = CHECK_CONDITION;\r
324                         scsiDev.target->state.sense.code = HARDWARE_ERROR;\r
325                         scsiDev.target->state.sense.asc = LOGICAL_UNIT_COMMUNICATION_FAILURE;\r
326                         scsiDev.phase = STATUS;\r
327                 }\r
328                 else\r
329                 {\r
330                         sdCmdNextLBA = tmpNextLBA;\r
331                         sdCmdState = CMD_STATE_READ;\r
332                         sdCmdTime = getTime_ms();\r
333                 }\r
334         }\r
335 }\r
336 \r
337 static void\r
338 dmaReadSector(uint8_t* outputBuffer)\r
339 {\r
340         // Wait for a start-block token.\r
341         // Don't wait more than 200ms.  The standard recommends 100ms.\r
342         uint32_t start = getTime_ms();\r
343         uint8_t token = sdSpiByte(0xFF);\r
344         trace(trace_spinSDBusy);\r
345         while (token != 0xFE && likely(elapsedTime_ms(start) <= 200))\r
346         {\r
347                 if (unlikely(token && ((token & 0xE0) == 0)))\r
348                 {\r
349                         // Error token!\r
350                         break;\r
351                 }\r
352                 token = sdSpiByte(0xFF);\r
353         }\r
354         if (unlikely(token != 0xFE))\r
355         {\r
356                 if (transfer.multiBlock)\r
357                 {\r
358                         sdCompleteRead();\r
359                 }\r
360                 if (scsiDev.status != CHECK_CONDITION)\r
361                 {\r
362                         scsiDev.status = CHECK_CONDITION;\r
363                         scsiDev.target->state.sense.code = HARDWARE_ERROR;\r
364                         scsiDev.target->state.sense.asc = UNRECOVERED_READ_ERROR;\r
365                         scsiDev.phase = STATUS;\r
366                 }\r
367                 sdClearStatus();\r
368                 return;\r
369         }\r
370 \r
371         static uint8_t dmaRxTd[2] = { CY_DMA_INVALID_TD, CY_DMA_INVALID_TD};\r
372         static uint8_t dmaTxTd = CY_DMA_INVALID_TD;\r
373         if (unlikely(dmaRxTd[0] == CY_DMA_INVALID_TD))\r
374         {\r
375                 dmaRxTd[0] = CyDmaTdAllocate();\r
376                 dmaRxTd[1] = CyDmaTdAllocate();\r
377                 dmaTxTd = CyDmaTdAllocate();\r
378 \r
379                 // Receive 512 bytes of data and then 2 bytes CRC.\r
380                 CyDmaTdSetConfiguration(dmaRxTd[0], SD_SECTOR_SIZE, dmaRxTd[1], TD_INC_DST_ADR);\r
381                 CyDmaTdSetConfiguration(dmaRxTd[1], 2, CY_DMA_DISABLE_TD, SD_RX_DMA__TD_TERMOUT_EN);\r
382                 CyDmaTdSetAddress(dmaRxTd[1], LO16((uint32)SDCard_RXDATA_PTR), LO16((uint32)&discardBuffer));\r
383 \r
384                 CyDmaTdSetConfiguration(dmaTxTd, SD_SECTOR_SIZE + 2, CY_DMA_DISABLE_TD, SD_TX_DMA__TD_TERMOUT_EN);\r
385                 CyDmaTdSetAddress(dmaTxTd, LO16((uint32)&dummyBuffer), LO16((uint32)SDCard_TXDATA_PTR));\r
386 \r
387         }\r
388         CyDmaTdSetAddress(dmaRxTd[0], LO16((uint32)SDCard_RXDATA_PTR), LO16((uint32)outputBuffer));\r
389 \r
390         sdIOState = SD_DMA;\r
391         sdTxDMAComplete = 0;\r
392         sdRxDMAComplete = 0;\r
393 \r
394         // Re-loading the initial TD's here is very important, or else\r
395         // we'll be re-using the last-used TD, which would be the last\r
396         // in the chain (ie. CRC TD)\r
397         CyDmaChSetInitialTd(sdDMARxChan, dmaRxTd[0]);\r
398         CyDmaChSetInitialTd(sdDMATxChan, dmaTxTd);\r
399 \r
400         // The DMA controller is a bit trigger-happy. It will retain\r
401         // a drq request that was triggered while the channel was\r
402         // disabled.\r
403         CyDmaChSetRequest(sdDMATxChan, CY_DMA_CPU_REQ);\r
404         CyDmaClearPendingDrq(sdDMARxChan);\r
405 \r
406         // There is no flow control, so we must ensure we can read the bytes\r
407         // before we start transmitting\r
408         CyDmaChEnable(sdDMARxChan, 1);\r
409         CyDmaChEnable(sdDMATxChan, 1);\r
410 }\r
411 \r
412 int\r
413 sdReadSectorDMAPoll()\r
414 {\r
415         if (sdRxDMAComplete && sdTxDMAComplete)\r
416         {\r
417                 // DMA transfer is complete\r
418                 sdIOState = SD_IDLE;\r
419                 return 1;\r
420         }\r
421         else\r
422         {\r
423                 return 0;\r
424         }\r
425 }\r
426 \r
427 void sdReadSingleSectorDMA(uint32_t lba, uint8_t* outputBuffer)\r
428 {\r
429         sdPreCmdState(CMD_STATE_READ);\r
430 \r
431         uint8 v;\r
432         if (!sdCard.ccs)\r
433         {\r
434                 lba = lba * SD_SECTOR_SIZE;\r
435         }\r
436         v = sdCommandAndResponse(SD_READ_SINGLE_BLOCK, lba);\r
437         if (unlikely(v))\r
438         {\r
439                 scsiDiskReset();\r
440                 sdClearStatus();\r
441 \r
442                 scsiDev.status = CHECK_CONDITION;\r
443                 scsiDev.target->state.sense.code = HARDWARE_ERROR;\r
444                 scsiDev.target->state.sense.asc = LOGICAL_UNIT_COMMUNICATION_FAILURE;\r
445                 scsiDev.phase = STATUS;\r
446         }\r
447         else\r
448         {\r
449                 dmaReadSector(outputBuffer);\r
450         }\r
451 }\r
452 \r
453 void\r
454 sdReadMultiSectorDMA(uint8_t* outputBuffer)\r
455 {\r
456         // Pre: sdReadMultiSectorPrep called.\r
457         dmaReadSector(outputBuffer);\r
458 }\r
459 \r
460 static void sdCompleteRead()\r
461 {\r
462         if (unlikely(sdIOState != SD_IDLE))\r
463         {\r
464                 // Not much choice but to wait until we've completed the transfer.\r
465                 // Cancelling the transfer can't be done as we have no way to reset\r
466                 // the SD card.\r
467                 trace(trace_spinSDCompleteRead);\r
468                 while (!sdReadSectorDMAPoll()) { /* spin */ }\r
469         }\r
470 \r
471 \r
472         if (sdCmdState == CMD_STATE_READ)\r
473         {\r
474                 uint8 r1b = sdCommandAndResponse(SD_STOP_TRANSMISSION, 0);\r
475 \r
476                 if (unlikely(r1b) && (scsiDev.phase == DATA_IN))\r
477                 {\r
478                         scsiDev.status = CHECK_CONDITION;\r
479                         scsiDev.target->state.sense.code = HARDWARE_ERROR;\r
480                         scsiDev.target->state.sense.asc = UNRECOVERED_READ_ERROR;\r
481                         scsiDev.phase = STATUS;\r
482                 }\r
483         }\r
484 \r
485         // R1b has an optional trailing "busy" signal, but we defer waiting on this.\r
486         // The next call so sdCommandAndResponse will wait for the busy state to\r
487         // clear.\r
488 \r
489         sdCmdState = CMD_STATE_IDLE;\r
490 }\r
491 \r
492 void\r
493 sdWriteMultiSectorDMA(uint8_t* outputBuffer)\r
494 {\r
495         static uint8_t dmaRxTd[2] = { CY_DMA_INVALID_TD, CY_DMA_INVALID_TD};\r
496         static uint8_t dmaTxTd[3] = { CY_DMA_INVALID_TD, CY_DMA_INVALID_TD, CY_DMA_INVALID_TD};\r
497         if (unlikely(dmaRxTd[0] == CY_DMA_INVALID_TD))\r
498         {\r
499                 dmaRxTd[0] = CyDmaTdAllocate();\r
500                 dmaRxTd[1] = CyDmaTdAllocate();\r
501                 dmaTxTd[0] = CyDmaTdAllocate();\r
502                 dmaTxTd[1] = CyDmaTdAllocate();\r
503                 dmaTxTd[2] = CyDmaTdAllocate();\r
504 \r
505                 // Transmit 512 bytes of data and then 2 bytes CRC, and then get the response byte\r
506                 // We need to do this without stopping the clock\r
507                 CyDmaTdSetConfiguration(dmaTxTd[0], 2, dmaTxTd[1], TD_INC_SRC_ADR);\r
508                 CyDmaTdSetAddress(dmaTxTd[0], LO16((uint32)&writeStartToken), LO16((uint32)SDCard_TXDATA_PTR));\r
509 \r
510                 CyDmaTdSetConfiguration(dmaTxTd[1], SD_SECTOR_SIZE, dmaTxTd[2], TD_INC_SRC_ADR);\r
511 \r
512                 CyDmaTdSetConfiguration(dmaTxTd[2], 2 + sizeof(writeResponseBuffer), CY_DMA_DISABLE_TD, SD_TX_DMA__TD_TERMOUT_EN);\r
513                 CyDmaTdSetAddress(dmaTxTd[2], LO16((uint32)&dummyBuffer), LO16((uint32)SDCard_TXDATA_PTR));\r
514 \r
515                 CyDmaTdSetConfiguration(dmaRxTd[0], SD_SECTOR_SIZE + 4, dmaRxTd[1], 0);\r
516                 CyDmaTdSetAddress(dmaRxTd[0], LO16((uint32)SDCard_RXDATA_PTR), LO16((uint32)&discardBuffer));\r
517                 CyDmaTdSetConfiguration(dmaRxTd[1], sizeof(writeResponseBuffer), CY_DMA_DISABLE_TD, SD_RX_DMA__TD_TERMOUT_EN|TD_INC_DST_ADR);\r
518                 CyDmaTdSetAddress(dmaRxTd[1], LO16((uint32)SDCard_RXDATA_PTR), LO16((uint32)&writeResponseBuffer));\r
519         }\r
520         CyDmaTdSetAddress(dmaTxTd[1], LO16((uint32)outputBuffer), LO16((uint32)SDCard_TXDATA_PTR));\r
521 \r
522 \r
523         sdIOState = SD_DMA;\r
524         // The DMA controller is a bit trigger-happy. It will retain\r
525         // a drq request that was triggered while the channel was\r
526         // disabled.\r
527         CyDmaChSetRequest(sdDMATxChan, CY_DMA_CPU_REQ);\r
528         CyDmaClearPendingDrq(sdDMARxChan);\r
529 \r
530         sdTxDMAComplete = 0;\r
531         sdRxDMAComplete = 0;\r
532 \r
533         // Re-loading the initial TD's here is very important, or else\r
534         // we'll be re-using the last-used TD, which would be the last\r
535         // in the chain (ie. CRC TD)\r
536         CyDmaChSetInitialTd(sdDMARxChan, dmaRxTd[0]);\r
537         CyDmaChSetInitialTd(sdDMATxChan, dmaTxTd[0]);\r
538 \r
539         // There is no flow control, so we must ensure we can read the bytes\r
540         // before we start transmitting\r
541         CyDmaChEnable(sdDMARxChan, 1);\r
542         CyDmaChEnable(sdDMATxChan, 1);\r
543 }\r
544 \r
545 int\r
546 sdWriteSectorDMAPoll()\r
547 {\r
548         if (sdRxDMAComplete && sdTxDMAComplete)\r
549         {\r
550                 if (sdIOState == SD_DMA)\r
551                 {\r
552                         // Retry a few times. The data token format is:\r
553                         // XXX0AAA1\r
554                         int i = 0;\r
555                         uint8_t dataToken;\r
556                         do\r
557                         {\r
558                                 dataToken = writeResponseBuffer[i]; // Response\r
559                                 ++i;\r
560                         } while (((dataToken & 0x0101) != 1) && (i < sizeof(writeResponseBuffer)));\r
561 \r
562                         // At this point we should either have an accepted token, or we'll\r
563                         // timeout and proceed into the error case below.\r
564                         if (unlikely(((dataToken & 0x1F) >> 1) != 0x2)) // Accepted.\r
565                         {\r
566                                 sdIOState = SD_IDLE;\r
567 \r
568                                 sdWaitWriteBusy();\r
569                                 sdSpiByte(0xFD); // STOP TOKEN\r
570                                 sdWaitWriteBusy();\r
571 \r
572                                 sdCmdState = CMD_STATE_IDLE;\r
573                                 scsiDiskReset();\r
574                                 sdClearStatus();\r
575 \r
576                                 scsiDev.status = CHECK_CONDITION;\r
577                                 scsiDev.target->state.sense.code = HARDWARE_ERROR;\r
578                                 scsiDev.target->state.sense.asc = LOGICAL_UNIT_COMMUNICATION_FAILURE;\r
579                                 scsiDev.phase = STATUS;\r
580                         }\r
581                         else\r
582                         {\r
583                                 sdIOState = SD_ACCEPTED;\r
584                         }\r
585                 }\r
586 \r
587                 if (sdIOState == SD_ACCEPTED)\r
588                 {\r
589                         // Wait while the SD card is busy\r
590                         if (sdSpiByte(0xFF) == 0xFF)\r
591                         {\r
592                                 sdIOState = SD_IDLE;\r
593                         }\r
594                 }\r
595 \r
596                 return sdIOState == SD_IDLE;\r
597         }\r
598         else\r
599         {\r
600                 return 0;\r
601         }\r
602 }\r
603 \r
604 static void sdCompleteWrite()\r
605 {\r
606         if (unlikely(sdIOState != SD_IDLE))\r
607         {\r
608                 // Not much choice but to wait until we've completed the transfer.\r
609                 // Cancelling the transfer can't be done as we have no way to reset\r
610                 // the SD card.\r
611                 trace(trace_spinSDCompleteWrite);\r
612                 while (!sdWriteSectorDMAPoll()) { /* spin */ }\r
613         }\r
614 \r
615         if (sdCmdState == CMD_STATE_WRITE)\r
616         {\r
617                 sdWaitWriteBusy();\r
618 \r
619                 sdSpiByte(0xFD); // STOP TOKEN\r
620 \r
621                 sdWaitWriteBusy();\r
622         }\r
623 \r
624 \r
625         if (likely(scsiDev.phase == DATA_OUT))\r
626         {\r
627                 uint16_t r2 = sdDoCommand(SD_SEND_STATUS, 0, 0, 1);\r
628                 if (unlikely(r2))\r
629                 {\r
630                         sdClearStatus();\r
631                         scsiDev.status = CHECK_CONDITION;\r
632                         scsiDev.target->state.sense.code = HARDWARE_ERROR;\r
633                         scsiDev.target->state.sense.asc = WRITE_ERROR_AUTO_REALLOCATION_FAILED;\r
634                         scsiDev.phase = STATUS;\r
635                 }\r
636         }\r
637         sdCmdState = CMD_STATE_IDLE;\r
638 }\r
639 \r
640 void sdCompleteTransfer()\r
641 {\r
642         sdPreCmdState(CMD_STATE_IDLE);\r
643 }\r
644 \r
645 \r
646 // SD Version 2 (SDHC) support\r
647 static int sendIfCond()\r
648 {\r
649         int retries = 50;\r
650 \r
651         do\r
652         {\r
653                 // 11:8 Host voltage. 1 = 2.7-3.6V\r
654                 // 7:0 Echo bits. Ignore.\r
655                 uint8 status = sdCRCCommandAndResponse(SD_SEND_IF_COND, 0x000001AA);\r
656 \r
657                 if (status == SD_R1_IDLE)\r
658                 {\r
659                         // Version 2 card.\r
660                         sdCard.version = 2;\r
661                         // Read 32bit response. Should contain the same bytes that\r
662                         // we sent in the command parameter.\r
663                         sdSpiByte(0xFF);\r
664                         sdSpiByte(0xFF);\r
665                         sdSpiByte(0xFF);\r
666                         sdSpiByte(0xFF);\r
667                         break;\r
668                 }\r
669                 else if (status & SD_R1_ILLEGAL)\r
670                 {\r
671                         // Version 1 card.\r
672                         sdCard.version = 1;\r
673                         sdClearStatus();\r
674                         break;\r
675                 }\r
676 \r
677                 sdClearStatus();\r
678         } while (--retries > 0);\r
679 \r
680         return retries > 0;\r
681 }\r
682 \r
683 static int sdOpCond()\r
684 {\r
685         uint32_t start = getTime_ms();\r
686 \r
687         uint8 status;\r
688         do\r
689         {\r
690                 sdCRCCommandAndResponse(SD_APP_CMD, 0);\r
691                 // Host Capacity Support = 1 (SDHC/SDXC supported)\r
692                 status = sdCRCCommandAndResponse(SD_APP_SEND_OP_COND, 0x40000000);\r
693 \r
694                 sdClearStatus();\r
695 \r
696         // Spec says to poll for 1 second.\r
697         } while ((status != 0) && (elapsedTime_ms(start) < 1000));\r
698 \r
699         return status == 0;\r
700 }\r
701 \r
702 static int sdReadOCR()\r
703 {\r
704         uint32_t start = getTime_ms();\r
705         int complete;\r
706         uint8 status;\r
707 \r
708         do\r
709         {\r
710                 uint8 buf[4];\r
711                 int i;\r
712 \r
713                 status = sdCRCCommandAndResponse(SD_READ_OCR, 0);\r
714                 if(status) { break; }\r
715 \r
716                 for (i = 0; i < 4; ++i)\r
717                 {\r
718                         buf[i] = sdSpiByte(0xFF);\r
719                 }\r
720 \r
721                 sdCard.ccs = (buf[0] & 0x40) ? 1 : 0;\r
722                 complete = (buf[0] & 0x80);\r
723 \r
724         } while (!status &&\r
725                 !complete &&\r
726                 (elapsedTime_ms(start) < 1000));\r
727 \r
728         return (status == 0) && complete;\r
729 }\r
730 \r
731 static void sdReadCID()\r
732 {\r
733         uint8 startToken;\r
734         int maxWait, i;\r
735 \r
736         uint8 status = sdCRCCommandAndResponse(SD_SEND_CID, 0);\r
737         if(status){return;}\r
738 \r
739         maxWait = 1023;\r
740         do\r
741         {\r
742                 startToken = sdSpiByte(0xFF);\r
743         } while(maxWait-- && (startToken != 0xFE));\r
744         if (startToken != 0xFE) { return; }\r
745 \r
746         for (i = 0; i < 16; ++i)\r
747         {\r
748                 sdCard.cid[i] = sdSpiByte(0xFF);\r
749         }\r
750         sdSpiByte(0xFF); // CRC\r
751         sdSpiByte(0xFF); // CRC\r
752 }\r
753 \r
754 static int sdReadCSD()\r
755 {\r
756         uint8 startToken;\r
757         int maxWait, i;\r
758 \r
759         uint8 status = sdCRCCommandAndResponse(SD_SEND_CSD, 0);\r
760         if(status){goto bad;}\r
761 \r
762         maxWait = 1023;\r
763         do\r
764         {\r
765                 startToken = sdSpiByte(0xFF);\r
766         } while(maxWait-- && (startToken != 0xFE));\r
767         if (startToken != 0xFE) { goto bad; }\r
768 \r
769         for (i = 0; i < 16; ++i)\r
770         {\r
771                 sdCard.csd[i] = sdSpiByte(0xFF);\r
772         }\r
773         sdSpiByte(0xFF); // CRC\r
774         sdSpiByte(0xFF); // CRC\r
775 \r
776         if ((sdCard.csd[0] >> 6) == 0x00)\r
777         {\r
778                 // CSD version 1\r
779                 // C_SIZE in bits [73:62]\r
780                 uint32 c_size = (((((uint32)sdCard.csd[6]) & 0x3) << 16) | (((uint32)sdCard.csd[7]) << 8) | sdCard.csd[8]) >> 6;\r
781                 uint32 c_mult = (((((uint32)sdCard.csd[9]) & 0x3) << 8) | ((uint32)sdCard.csd[0xa])) >> 7;\r
782                 uint32 sectorSize = sdCard.csd[5] & 0x0F;\r
783                 sdCard.capacity = ((c_size+1) * ((uint64)1 << (c_mult+2)) * ((uint64)1 << sectorSize)) / SD_SECTOR_SIZE;\r
784         }\r
785         else if ((sdCard.csd[0] >> 6) == 0x01)\r
786         {\r
787                 // CSD version 2\r
788                 // C_SIZE in bits [69:48]\r
789 \r
790                 uint32 c_size =\r
791                         ((((uint32)sdCard.csd[7]) & 0x3F) << 16) |\r
792                         (((uint32)sdCard.csd[8]) << 8) |\r
793                         ((uint32)sdCard.csd[7]);\r
794                 sdCard.capacity = (c_size + 1) * 1024;\r
795         }\r
796         else\r
797         {\r
798                 goto bad;\r
799         }\r
800 \r
801         return 1;\r
802 bad:\r
803         return 0;\r
804 }\r
805 \r
806 static void sdInitDMA()\r
807 {\r
808         // One-time init only.\r
809         if (sdDMATxChan == CY_DMA_INVALID_CHANNEL)\r
810         {\r
811                 sdDMATxChan =\r
812                         SD_TX_DMA_DmaInitialize(\r
813                                 2, // Bytes per burst\r
814                                 1, // request per burst\r
815                                 HI16(CYDEV_SRAM_BASE),\r
816                                 HI16(CYDEV_PERIPH_BASE)\r
817                                 );\r
818 \r
819                 sdDMARxChan =\r
820                         SD_RX_DMA_DmaInitialize(\r
821                                 1, // Bytes per burst\r
822                                 1, // request per burst\r
823                                 HI16(CYDEV_PERIPH_BASE),\r
824                                 HI16(CYDEV_SRAM_BASE)\r
825                                 );\r
826 \r
827                 CyDmaChDisable(sdDMATxChan);\r
828                 CyDmaChDisable(sdDMARxChan);\r
829 \r
830                 SD_RX_DMA_COMPLETE_StartEx(sdRxISR);\r
831                 SD_TX_DMA_COMPLETE_StartEx(sdTxISR);\r
832         }\r
833 }\r
834 \r
835 int sdInit()\r
836 {\r
837         int result = 0;\r
838         int i;\r
839         uint8 v;\r
840 \r
841         sdCmdState = CMD_STATE_IDLE;\r
842         sdCard.version = 0;\r
843         sdCard.ccs = 0;\r
844         sdCard.capacity = 0;\r
845         memset(sdCard.csd, 0, sizeof(sdCard.csd));\r
846         memset(sdCard.cid, 0, sizeof(sdCard.cid));\r
847 \r
848         sdInitDMA();\r
849 \r
850         SD_CS_SetDriveMode(SD_CS_DM_STRONG);\r
851         SD_CS_Write(1); // Set CS inactive (active low)\r
852 \r
853         // Set the SPI clock for 400kHz transfers\r
854         // 25MHz / 400kHz approx factor of 63.\r
855         // The register contains (divider - 1)\r
856         uint16_t clkDiv25MHz =  SD_Data_Clk_GetDividerRegister();\r
857         SD_Data_Clk_SetDivider(((clkDiv25MHz + 1) * 63) - 1);\r
858         // Wait for the clock to settle.\r
859         CyDelayUs(1);\r
860 \r
861         SDCard_Start(); // Enable SPI hardware\r
862 \r
863         // Power on sequence. 74 clock cycles of a "1" while CS unasserted.\r
864         for (i = 0; i < 10; ++i)\r
865         {\r
866                 sdSpiByte(0xFF);\r
867         }\r
868 \r
869         SD_CS_Write(0); // Set CS active (active low)\r
870         CyDelayUs(1);\r
871 \r
872         sdSpiByte(0xFF);\r
873         v = sdDoCommand(SD_GO_IDLE_STATE, 0, 1, 0);\r
874         if(v != 1){goto bad;}\r
875 \r
876         ledOn();\r
877         if (!sendIfCond()) goto bad; // Sets V1 or V2 flag  CMD8\r
878         if (!sdOpCond()) goto bad; // ACMD41. Wait for init completes.\r
879         if (!sdReadOCR()) goto bad; // CMD58. Get CCS flag. Only valid after init.\r
880 \r
881         // This command will be ignored if sdCard.ccs is set.\r
882         // SDHC and SDXC are always 512bytes.\r
883         v = sdCRCCommandAndResponse(SD_SET_BLOCKLEN, SD_SECTOR_SIZE); //Force sector size\r
884         if(v){goto bad;}\r
885         v = sdCRCCommandAndResponse(SD_CRC_ON_OFF, 0); //crc off\r
886         if(v){goto bad;}\r
887 \r
888         // now set the sd card back to full speed.\r
889         // The SD Card spec says we can run SPI @ 25MHz\r
890         SDCard_Stop();\r
891 \r
892         // We can't run at full-speed with the pullup resistors enabled.\r
893         SD_MISO_SetDriveMode(SD_MISO_DM_DIG_HIZ);\r
894         SD_MOSI_SetDriveMode(SD_MOSI_DM_STRONG);\r
895         SD_SCK_SetDriveMode(SD_SCK_DM_STRONG);\r
896 \r
897         SD_Data_Clk_SetDivider(clkDiv25MHz);\r
898         CyDelayUs(1);\r
899         SDCard_Start();\r
900 \r
901         // Clear out rubbish data through clock change\r
902         CyDelayUs(1);\r
903         SDCard_ReadRxStatus();\r
904         SDCard_ReadTxStatus();\r
905         SDCard_ClearFIFO();\r
906 \r
907         if (!sdReadCSD()) goto bad;\r
908         sdReadCID();\r
909 \r
910         result = 1;\r
911         goto out;\r
912 \r
913 bad:\r
914         SD_Data_Clk_SetDivider(clkDiv25MHz); // Restore the clock for our next retry\r
915         sdCard.capacity = 0;\r
916 \r
917 out:\r
918         sdClearStatus();\r
919         ledOff();\r
920         return result;\r
921 \r
922 }\r
923 \r
924 void sdWriteMultiSectorPrep(uint32_t sdLBA, uint32_t sdSectors)\r
925 {\r
926         uint32_t tmpNextLBA = sdLBA + sdSectors;\r
927 \r
928         if (!sdCard.ccs)\r
929         {\r
930                 sdLBA = sdLBA * SD_SECTOR_SIZE;\r
931                 tmpNextLBA = tmpNextLBA * SD_SECTOR_SIZE;\r
932         }\r
933 \r
934         if (sdCmdState == CMD_STATE_WRITE && sdCmdNextLBA == sdLBA)\r
935         {\r
936                 // Well, that was lucky. We're already writing this data\r
937                 sdCmdNextLBA = tmpNextLBA;\r
938                 sdCmdTime = getTime_ms();\r
939         }\r
940         else\r
941         {\r
942                 sdPreCmdState(CMD_STATE_WRITE);\r
943 \r
944                 // Set the number of blocks to pre-erase by the multiple block write\r
945                 // command. We don't care about the response - if the command is not\r
946                 // accepted, writes will just be a bit slower. Max 22bit parameter.\r
947                 uint32 blocks = sdSectors > 0x7FFFFF ? 0x7FFFFF : sdSectors;\r
948                 sdCommandAndResponse(SD_APP_CMD, 0);\r
949                 sdCommandAndResponse(SD_APP_SET_WR_BLK_ERASE_COUNT, blocks);\r
950 \r
951                 uint8_t v = sdCommandAndResponse(SD_WRITE_MULTIPLE_BLOCK, sdLBA);\r
952                 if (unlikely(v))\r
953                 {\r
954                         scsiDiskReset();\r
955                         sdClearStatus();\r
956                         scsiDev.status = CHECK_CONDITION;\r
957                         scsiDev.target->state.sense.code = HARDWARE_ERROR;\r
958                         scsiDev.target->state.sense.asc = LOGICAL_UNIT_COMMUNICATION_FAILURE;\r
959                         scsiDev.phase = STATUS;\r
960                 }\r
961                 else\r
962                 {\r
963                         sdCmdTime = getTime_ms();\r
964                         sdCmdNextLBA = tmpNextLBA;\r
965                         sdCmdState = CMD_STATE_WRITE;\r
966                 }\r
967         }\r
968 }\r
969 \r
970 void sdPoll()\r
971 {\r
972         if ((scsiDev.phase == BUS_FREE) &&\r
973                 (sdCmdState != CMD_STATE_IDLE) &&\r
974                 (elapsedTime_ms(sdCmdTime) >= 50))\r
975         {\r
976                 sdPreCmdState(CMD_STATE_IDLE);\r
977         }\r
978 }\r
979 \r
980 static int\r
981 sdIsCardPresent()\r
982 {\r
983         // The CS line is pulled high by the SD card.\r
984         // De-assert the line, and check if it's high.\r
985         // This isn't foolproof as it'll be left floating without\r
986         // an SD card. We can't use the built-in pull-down resistor as it will\r
987         // overpower the SD pullup resistor.\r
988         SD_CS_Write(0);\r
989         SD_CS_SetDriveMode(SD_CS_DM_DIG_HIZ);\r
990 \r
991         // Delay extended to work with 60cm cables running cards at 2.85V\r
992         CyDelayCycles(128);\r
993         int cs = SD_CS_Read();\r
994         SD_CS_SetDriveMode(SD_CS_DM_STRONG);\r
995         return cs;\r
996 }\r
997 \r
998 void sdCheckPresent()\r
999 {\r
1000         static int firstCheck = 1;\r
1001         // Check if there's an SD card present.\r
1002         if ((scsiDev.phase == BUS_FREE) &&\r
1003                 (sdIOState == SD_IDLE) &&\r
1004                 (sdCmdState == CMD_STATE_IDLE))\r
1005         {\r
1006                 int cs = sdIsCardPresent();\r
1007 \r
1008                 if (cs && !(sdCard.dev.mediaState & MEDIA_PRESENT))\r
1009                 {\r
1010                         static int firstInit = 1;\r
1011 \r
1012                         // Debounce. Quicker if the card is present at\r
1013                         // power on\r
1014                         for (int i = 0; cs && (i < (firstCheck ? 2 : 50)); ++i)\r
1015                         {\r
1016                                 cs = sdIsCardPresent();\r
1017                                 CyDelay(5);\r
1018                         }\r
1019 \r
1020                         if (cs && sdInit())\r
1021                         {\r
1022                                 sdCard.dev.mediaState |= MEDIA_PRESENT | MEDIA_INITIALISED;\r
1023 \r
1024                                 // Always "start" the device. Many systems (eg. Apple System 7)\r
1025                                 // won't respond properly to\r
1026                                 // LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED sense\r
1027                                 // code, even if they stopped it first with\r
1028                                 // START STOP UNIT command.\r
1029                                 sdCard.dev.mediaState |= MEDIA_STARTED;\r
1030 \r
1031                                 if (!firstInit)\r
1032                                 {\r
1033                                         int i;\r
1034                                         for (i = 0; i < MAX_SCSI_TARGETS; ++i)\r
1035                                         {\r
1036                                                 sdCard.targets[i].state.unitAttention = PARAMETERS_CHANGED;\r
1037                                         }\r
1038                                 }\r
1039                                 firstInit = 0;\r
1040                         }\r
1041                 }\r
1042                 else if (!cs && (sdCard.dev.mediaState & MEDIA_PRESENT))\r
1043                 {\r
1044                         sdCard.capacity = 0;\r
1045                         sdCard.dev.mediaState &= ~MEDIA_PRESENT;\r
1046                         sdCard.dev.mediaState &= ~MEDIA_INITIALISED;\r
1047                         int i;\r
1048                         for (i = 0; i < MAX_SCSI_TARGETS; ++i)\r
1049                         {\r
1050                                 sdCard.targets[i].state.unitAttention = PARAMETERS_CHANGED;\r
1051                         }\r
1052                 }\r
1053         }\r
1054         firstCheck = 0;\r
1055 }\r
1056 \r
1057 static void sd_earlyInit(S2S_Device* dev)\r
1058 {\r
1059         SdCard* sdCardDevice = (SdCard*)dev;\r
1060 \r
1061         for (int i = 0; i < MAX_SCSI_TARGETS; ++i)\r
1062         {\r
1063                 sdCardDevice->targets[i].device = dev;\r
1064         \r
1065         const S2S_TargetCfg* cfg = getConfigByIndex(i);\r
1066         if (cfg->storageDevice == CONFIG_STOREDEVICE_SD)\r
1067         {\r
1068                     sdCardDevice->targets[i].cfg = (S2S_TargetCfg*)cfg;\r
1069         }\r
1070         else\r
1071         {\r
1072             sdCardDevice->targets[i].cfg = NULL;\r
1073         }\r
1074         }\r
1075         sdCardDevice->lastPollMediaTime = getTime_ms();\r
1076 \r
1077         // Don't require the host to send us a START STOP UNIT command\r
1078         sdCardDevice->dev.mediaState = MEDIA_STARTED;\r
1079 }\r
1080 \r
1081 static void sd_deviceInit(S2S_Device* dev)\r
1082 {\r
1083         sdCheckPresent();\r
1084 }\r
1085 \r
1086 static S2S_Target* sd_getTargets(S2S_Device* dev, int* count)\r
1087 {\r
1088         SdCard* sdCardDevice = (SdCard*)dev;\r
1089         *count = MAX_SCSI_TARGETS;\r
1090         return sdCardDevice->targets;\r
1091 }\r
1092 \r
1093 static uint32_t sd_getCapacity(S2S_Device* dev)\r
1094 {\r
1095         SdCard* sdCardDevice = (SdCard*)dev;\r
1096         return sdCardDevice->capacity;\r
1097 }\r
1098 \r
1099 static int sd_pollMediaChange(S2S_Device* dev)\r
1100 {\r
1101         SdCard* sdCardDevice = (SdCard*)dev;\r
1102         if (elapsedTime_ms(sdCardDevice->lastPollMediaTime) > 200)\r
1103         {\r
1104                 sdCheckPresent();\r
1105                 sdCardDevice->lastPollMediaTime = getTime_ms();\r
1106                 return 0;\r
1107         }\r
1108         else\r
1109         {\r
1110                 return 0;\r
1111         }\r
1112 }\r
1113 \r
1114 static void sd_pollMediaBusy(S2S_Device* dev)\r
1115 {\r
1116         SdCard* sdCardDevice = (SdCard*)dev;\r
1117         sdCardDevice->lastPollMediaTime = getTime_ms();\r
1118 }\r
1119 \r
1120 static void sd_erase(S2S_Device* dev, uint32_t sectorNumber, uint32_t count)\r
1121 {\r
1122     // TODO\r
1123 }\r
1124 \r
1125 static void sd_read(S2S_Device* dev, uint32_t sectorNumber, uint32_t count, uint8_t* buffer)\r
1126 {\r
1127     // TODO\r
1128 }\r
1129 \r
1130 static void sd_readAsync(S2S_Device* dev, uint32_t sectorNumber, uint32_t count, uint8_t* buffer)\r
1131 {\r
1132     // TODO\r
1133 }\r
1134 \r
1135 \r
1136 static int sd_readAsyncPoll(S2S_Device* dev)\r
1137 {\r
1138     return 1;\r
1139 }\r
1140 \r
1141 \r
1142 static void sd_write(S2S_Device* dev, uint32_t sectorNumber, uint32_t count, const uint8_t* buffer)\r
1143 {\r
1144     // TODO\r
1145 }\r