Fix bug in using nor flash storage without SD
[SCSI2SD.git] / software / SCSI2SD / src / disk.c
1 //      Copyright (C) 2013 Michael McMaster <michael@codesrc.com>\r
2 //      Copyright (C) 2014 Doug Brown <doug@downtowndougbrown.com>\r
3 //\r
4 //      This file is part of SCSI2SD.\r
5 //\r
6 //      SCSI2SD is free software: you can redistribute it and/or modify\r
7 //      it under the terms of the GNU General Public License as published by\r
8 //      the Free Software Foundation, either version 3 of the License, or\r
9 //      (at your option) any later version.\r
10 //\r
11 //      SCSI2SD is distributed in the hope that it will be useful,\r
12 //      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 //      GNU General Public License for more details.\r
15 //\r
16 //      You should have received a copy of the GNU General Public License\r
17 //      along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.\r
18 #include "device.h"\r
19 #include "scsi.h"\r
20 #include "scsiPhy.h"\r
21 #include "config.h"\r
22 #include "debug.h"\r
23 #include "debug.h"\r
24 #include "disk.h"\r
25 #include "sd.h"\r
26 #include "time.h"\r
27 \r
28 #include <string.h>\r
29 \r
30 // Global\r
31 Transfer transfer;\r
32 \r
33 // Callback once all data has been read in the data out phase.\r
34 static void doFormatUnitComplete(void)\r
35 {\r
36         // TODO start writing the initialisation pattern to the SD\r
37         // card\r
38         scsiDev.phase = STATUS;\r
39 }\r
40 \r
41 static void doFormatUnitSkipData(int bytes)\r
42 {\r
43         // We may not have enough memory to store the initialisation pattern and\r
44         // defect list data.  Since we're not making use of it yet anyway, just\r
45         // discard the bytes.\r
46         scsiEnterPhase(DATA_OUT);\r
47         int i;\r
48         for (i = 0; i < bytes; ++i)\r
49         {\r
50                 scsiReadByte();\r
51         }\r
52 }\r
53 \r
54 // Callback from the data out phase.\r
55 static void doFormatUnitPatternHeader(void)\r
56 {\r
57         int defectLength =\r
58                 ((((uint16_t)scsiDev.data[2])) << 8) +\r
59                         scsiDev.data[3];\r
60 \r
61         int patternLength =\r
62                 ((((uint16_t)scsiDev.data[4 + 2])) << 8) +\r
63                 scsiDev.data[4 + 3];\r
64 \r
65                 doFormatUnitSkipData(defectLength + patternLength);\r
66                 doFormatUnitComplete();\r
67 }\r
68 \r
69 // Callback from the data out phase.\r
70 static void doFormatUnitHeader(void)\r
71 {\r
72         int IP = (scsiDev.data[1] & 0x08) ? 1 : 0;\r
73         int DSP = (scsiDev.data[1] & 0x04) ? 1 : 0;\r
74 \r
75         if (! DSP) // disable save parameters\r
76         {\r
77                 // Save the "MODE SELECT savable parameters"\r
78                 configSave(\r
79                         scsiDev.target->cfg->scsiId & CONFIG_TARGET_ID_BITS,\r
80                         scsiDev.target->state.bytesPerSector);\r
81         }\r
82 \r
83         if (IP)\r
84         {\r
85                 // We need to read the initialisation pattern header first.\r
86                 scsiDev.dataLen += 4;\r
87                 scsiDev.phase = DATA_OUT;\r
88                 scsiDev.postDataOutHook = doFormatUnitPatternHeader;\r
89         }\r
90         else\r
91         {\r
92                 // Read the defect list data\r
93                 int defectLength =\r
94                         ((((uint16_t)scsiDev.data[2])) << 8) +\r
95                         scsiDev.data[3];\r
96                 doFormatUnitSkipData(defectLength);\r
97                 doFormatUnitComplete();\r
98         }\r
99 }\r
100 \r
101 static void doReadCapacity()\r
102 {\r
103         uint32_t lba = (((uint32) scsiDev.cdb[2]) << 24) +\r
104                 (((uint32) scsiDev.cdb[3]) << 16) +\r
105                 (((uint32) scsiDev.cdb[4]) << 8) +\r
106                 scsiDev.cdb[5];\r
107         int pmi = scsiDev.cdb[8] & 1;\r
108 \r
109         uint32_t capacity = getScsiCapacity(\r
110                 scsiDev.target->device,\r
111                 scsiDev.target->cfg->sdSectorStart,\r
112                 scsiDev.target->state.bytesPerSector,\r
113                 scsiDev.target->cfg->scsiSectors);\r
114 \r
115         if (!pmi && lba)\r
116         {\r
117                 // error.\r
118                 // We don't do anything with the "partial medium indicator", and\r
119                 // assume that delays are constant across each block. But the spec\r
120                 // says we must return this error if pmi is specified incorrectly.\r
121                 scsiDev.status = CHECK_CONDITION;\r
122                 scsiDev.target->state.sense.code = ILLEGAL_REQUEST;\r
123                 scsiDev.target->state.sense.asc = INVALID_FIELD_IN_CDB;\r
124                 scsiDev.phase = STATUS;\r
125         }\r
126         else if (capacity > 0)\r
127         {\r
128                 uint32_t highestBlock = capacity - 1;\r
129 \r
130                 scsiDev.data[0] = highestBlock >> 24;\r
131                 scsiDev.data[1] = highestBlock >> 16;\r
132                 scsiDev.data[2] = highestBlock >> 8;\r
133                 scsiDev.data[3] = highestBlock;\r
134 \r
135                 uint32_t bytesPerSector = scsiDev.target->state.bytesPerSector;\r
136                 scsiDev.data[4] = bytesPerSector >> 24;\r
137                 scsiDev.data[5] = bytesPerSector >> 16;\r
138                 scsiDev.data[6] = bytesPerSector >> 8;\r
139                 scsiDev.data[7] = bytesPerSector;\r
140                 scsiDev.dataLen = 8;\r
141                 scsiDev.phase = DATA_IN;\r
142         }\r
143         else\r
144         {\r
145                 scsiDev.status = CHECK_CONDITION;\r
146                 scsiDev.target->state.sense.code = NOT_READY;\r
147                 scsiDev.target->state.sense.asc = MEDIUM_NOT_PRESENT;\r
148                 scsiDev.phase = STATUS;\r
149         }\r
150 }\r
151 \r
152 static void doWrite(uint32 lba, uint32 blocks)\r
153 {\r
154         if (unlikely(scsiDev.target->cfg->deviceType == CONFIG_FLOPPY_14MB)) {\r
155                 // Floppies are supposed to be slow. Some systems can't handle a floppy\r
156                 // without an access time\r
157                 CyDelay(10);\r
158         }\r
159 \r
160         uint32_t bytesPerSector = scsiDev.target->state.bytesPerSector;\r
161         MEDIA_STATE* mediaState = &(scsiDev.target->device->mediaState);\r
162 \r
163         if (unlikely(*mediaState & MEDIA_WP) ||\r
164                 unlikely(scsiDev.target->cfg->deviceType == CONFIG_OPTICAL) ||\r
165         (scsiDev.target->cfg->storageDevice != CONFIG_STOREDEVICE_SD))\r
166 \r
167         {\r
168                 scsiDev.status = CHECK_CONDITION;\r
169                 scsiDev.target->state.sense.code = ILLEGAL_REQUEST;\r
170                 scsiDev.target->state.sense.asc = WRITE_PROTECTED;\r
171                 scsiDev.phase = STATUS;\r
172         }\r
173         else if (unlikely(((uint64) lba) + blocks >\r
174                 getScsiCapacity(\r
175                         scsiDev.target->device,\r
176                         scsiDev.target->cfg->sdSectorStart,\r
177                         bytesPerSector,\r
178                         scsiDev.target->cfg->scsiSectors\r
179                         )\r
180                 ))\r
181         {\r
182                 scsiDev.status = CHECK_CONDITION;\r
183                 scsiDev.target->state.sense.code = ILLEGAL_REQUEST;\r
184                 scsiDev.target->state.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;\r
185                 scsiDev.phase = STATUS;\r
186         }\r
187         else\r
188         {\r
189                 transfer.lba = lba;\r
190                 transfer.blocks = blocks;\r
191                 transfer.currentBlock = 0;\r
192                 scsiDev.phase = DATA_OUT;\r
193                 scsiDev.dataLen = bytesPerSector;\r
194                 scsiDev.dataPtr = bytesPerSector;\r
195 \r
196                 // No need for single-block writes atm.  Overhead of the\r
197                 // multi-block write is minimal.\r
198                 transfer.multiBlock = 1;\r
199 \r
200 \r
201                 uint32_t sdLBA =\r
202                         SCSISector2SD(\r
203                                 scsiDev.target->cfg->sdSectorStart,\r
204                                 bytesPerSector,\r
205                                 lba);\r
206                 uint32_t sdBlocks = blocks * SDSectorsPerSCSISector(bytesPerSector);\r
207                 sdWriteMultiSectorPrep(sdLBA, sdBlocks);\r
208         }\r
209 }\r
210 \r
211 \r
212 static void doRead(uint32 lba, uint32 blocks)\r
213 {\r
214         if (unlikely(scsiDev.target->cfg->deviceType == CONFIG_FLOPPY_14MB)) {\r
215                 // Floppies are supposed to be slow. Some systems can't handle a floppy\r
216                 // without an access time\r
217                 CyDelay(10);\r
218         }\r
219 \r
220         uint32_t capacity = getScsiCapacity(\r
221         scsiDev.target->device,\r
222                 scsiDev.target->cfg->sdSectorStart,\r
223                 scsiDev.target->state.bytesPerSector,\r
224                 scsiDev.target->cfg->scsiSectors);\r
225         if (unlikely(((uint64) lba) + blocks > capacity))\r
226         {\r
227                 scsiDev.status = CHECK_CONDITION;\r
228                 scsiDev.target->state.sense.code = ILLEGAL_REQUEST;\r
229                 scsiDev.target->state.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;\r
230                 scsiDev.phase = STATUS;\r
231         }\r
232         else\r
233         {\r
234                 transfer.lba = lba;\r
235                 transfer.blocks = blocks;\r
236                 transfer.currentBlock = 0;\r
237                 scsiDev.phase = DATA_IN;\r
238                 scsiDev.dataLen = 0; // No data yet\r
239 \r
240                 uint32_t bytesPerSector = scsiDev.target->state.bytesPerSector;\r
241                 uint32_t sdSectorPerSCSISector = SDSectorsPerSCSISector(bytesPerSector);\r
242                 uint32_t sdSectors =\r
243                         blocks * sdSectorPerSCSISector;\r
244 \r
245                 if ((\r
246                                 (sdSectors == 1) &&\r
247                                 !(scsiDev.boardCfg.flags & CONFIG_ENABLE_CACHE)\r
248                         ) ||\r
249                         unlikely(((uint64) lba) + blocks == capacity) ||\r
250             (scsiDev.target->cfg->storageDevice != CONFIG_STOREDEVICE_SD)\r
251                         )\r
252                 {\r
253                         // We get errors on reading the last sector using a multi-sector\r
254                         // read :-(\r
255                         transfer.multiBlock = 0;\r
256                 }\r
257                 else\r
258                 {\r
259                         transfer.multiBlock = 1;\r
260 \r
261                         uint32_t sdLBA =\r
262                                 SCSISector2SD(\r
263                                         scsiDev.target->cfg->sdSectorStart,\r
264                                         bytesPerSector,\r
265                                         lba);\r
266 \r
267                         sdReadMultiSectorPrep(sdLBA, sdSectors);\r
268                 }\r
269         }\r
270 }\r
271 \r
272 static void doSeek(uint32 lba)\r
273 {\r
274         if (lba >=\r
275                 getScsiCapacity(\r
276             scsiDev.target->device,\r
277                         scsiDev.target->cfg->sdSectorStart,\r
278                         scsiDev.target->state.bytesPerSector,\r
279                         scsiDev.target->cfg->scsiSectors)\r
280                 )\r
281         {\r
282                 scsiDev.status = CHECK_CONDITION;\r
283                 scsiDev.target->state.sense.code = ILLEGAL_REQUEST;\r
284                 scsiDev.target->state.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;\r
285                 scsiDev.phase = STATUS;\r
286         }\r
287         else\r
288         {\r
289                 CyDelay(10);\r
290         }\r
291 }\r
292 \r
293 static int doTestUnitReady()\r
294 {\r
295         MEDIA_STATE* mediaState = &(scsiDev.target->device->mediaState);\r
296 \r
297         int ready = 1;\r
298         if (likely(*mediaState == (MEDIA_STARTED | MEDIA_PRESENT | MEDIA_INITIALISED)))\r
299         {\r
300                 // nothing to do.\r
301         }\r
302         else if (unlikely(!(*mediaState & MEDIA_STARTED)))\r
303         {\r
304                 ready = 0;\r
305                 scsiDev.status = CHECK_CONDITION;\r
306                 scsiDev.target->state.sense.code = NOT_READY;\r
307                 scsiDev.target->state.sense.asc = LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED;\r
308                 scsiDev.phase = STATUS;\r
309         }\r
310         else if (unlikely(!(*mediaState & MEDIA_PRESENT)))\r
311         {\r
312                 ready = 0;\r
313                 scsiDev.status = CHECK_CONDITION;\r
314                 scsiDev.target->state.sense.code = NOT_READY;\r
315                 scsiDev.target->state.sense.asc = MEDIUM_NOT_PRESENT;\r
316                 scsiDev.phase = STATUS;\r
317         }\r
318         else if (unlikely(!(*mediaState & MEDIA_INITIALISED)))\r
319         {\r
320                 ready = 0;\r
321                 scsiDev.status = CHECK_CONDITION;\r
322                 scsiDev.target->state.sense.code = NOT_READY;\r
323                 scsiDev.target->state.sense.asc = LOGICAL_UNIT_NOT_READY_CAUSE_NOT_REPORTABLE;\r
324                 scsiDev.phase = STATUS;\r
325         }\r
326         return ready;\r
327 }\r
328 \r
329 // Handle direct-access scsi device commands\r
330 int scsiDiskCommand()\r
331 {\r
332         int commandHandled = 1;\r
333 \r
334         uint8 command = scsiDev.cdb[0];\r
335         if (unlikely(command == 0x1B))\r
336         {\r
337                 // START STOP UNIT\r
338                 // Enable or disable media access operations.\r
339                 // Ignore load/eject requests. We can't do that.\r
340                 //int immed = scsiDev.cdb[1] & 1;\r
341                 int start = scsiDev.cdb[4] & 1;\r
342 \r
343                 MEDIA_STATE* mediaState = &(scsiDev.target->device->mediaState);\r
344                 if (start)\r
345                 {\r
346                         *mediaState = *mediaState | MEDIA_STARTED;\r
347                         if (!(*mediaState & MEDIA_INITIALISED))\r
348                         {\r
349                                 if (*mediaState & MEDIA_PRESENT)\r
350                                 {\r
351                                         *mediaState = *mediaState | MEDIA_INITIALISED;\r
352                                 }\r
353                         }\r
354                 }\r
355                 else\r
356                 {\r
357                         *mediaState &= ~MEDIA_STARTED;\r
358                 }\r
359         }\r
360         else if (unlikely(command == 0x00))\r
361         {\r
362                 // TEST UNIT READY\r
363                 doTestUnitReady();\r
364         }\r
365         else if (unlikely(!doTestUnitReady()))\r
366         {\r
367                 // Status and sense codes already set by doTestUnitReady\r
368         }\r
369         else if (likely(command == 0x08))\r
370         {\r
371                 // READ(6)\r
372                 uint32 lba =\r
373                         (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +\r
374                         (((uint32) scsiDev.cdb[2]) << 8) +\r
375                         scsiDev.cdb[3];\r
376                 uint32 blocks = scsiDev.cdb[4];\r
377                 if (unlikely(blocks == 0)) blocks = 256;\r
378                 doRead(lba, blocks);\r
379         }\r
380         else if (likely(command == 0x28))\r
381         {\r
382                 // READ(10)\r
383                 // Ignore all cache control bits - we don't support a memory cache.\r
384 \r
385                 uint32 lba =\r
386                         (((uint32) scsiDev.cdb[2]) << 24) +\r
387                         (((uint32) scsiDev.cdb[3]) << 16) +\r
388                         (((uint32) scsiDev.cdb[4]) << 8) +\r
389                         scsiDev.cdb[5];\r
390                 uint32 blocks =\r
391                         (((uint32) scsiDev.cdb[7]) << 8) +\r
392                         scsiDev.cdb[8];\r
393 \r
394                 doRead(lba, blocks);\r
395         }\r
396         else if (likely(command == 0x0A))\r
397         {\r
398                 // WRITE(6)\r
399                 uint32 lba =\r
400                         (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +\r
401                         (((uint32) scsiDev.cdb[2]) << 8) +\r
402                         scsiDev.cdb[3];\r
403                 uint32 blocks = scsiDev.cdb[4];\r
404                 if (unlikely(blocks == 0)) blocks = 256;\r
405                 doWrite(lba, blocks);\r
406         }\r
407         else if (likely(command == 0x2A) || // WRITE(10)\r
408                 unlikely(command == 0x2E)) // WRITE AND VERIFY\r
409         {\r
410                 // Ignore all cache control bits - we don't support a memory cache.\r
411                 // Don't bother verifying either. The SD card likely stores ECC\r
412                 // along with each flash row.\r
413 \r
414                 uint32 lba =\r
415                         (((uint32) scsiDev.cdb[2]) << 24) +\r
416                         (((uint32) scsiDev.cdb[3]) << 16) +\r
417                         (((uint32) scsiDev.cdb[4]) << 8) +\r
418                         scsiDev.cdb[5];\r
419                 uint32 blocks =\r
420                         (((uint32) scsiDev.cdb[7]) << 8) +\r
421                         scsiDev.cdb[8];\r
422 \r
423                 doWrite(lba, blocks);\r
424         }\r
425 \r
426         else if (unlikely(command == 0x04))\r
427         {\r
428                 // FORMAT UNIT\r
429                 // We don't really do any formatting, but we need to read the correct\r
430                 // number of bytes in the DATA_OUT phase to make the SCSI host happy.\r
431 \r
432                 int fmtData = (scsiDev.cdb[1] & 0x10) ? 1 : 0;\r
433                 if (fmtData)\r
434                 {\r
435                         // We need to read the parameter list, but we don't know how\r
436                         // big it is yet. Start with the header.\r
437                         scsiDev.dataLen = 4;\r
438                         scsiDev.phase = DATA_OUT;\r
439                         scsiDev.postDataOutHook = doFormatUnitHeader;\r
440                 }\r
441                 else\r
442                 {\r
443                         // No data to read, we're already finished!\r
444                 }\r
445         }\r
446         else if (unlikely(command == 0x25))\r
447         {\r
448                 // READ CAPACITY\r
449                 doReadCapacity();\r
450         }\r
451         else if (unlikely(command == 0x0B))\r
452         {\r
453                 // SEEK(6)\r
454                 uint32 lba =\r
455                         (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +\r
456                         (((uint32) scsiDev.cdb[2]) << 8) +\r
457                         scsiDev.cdb[3];\r
458 \r
459                 doSeek(lba);\r
460         }\r
461 \r
462         else if (unlikely(command == 0x2B))\r
463         {\r
464                 // SEEK(10)\r
465                 uint32 lba =\r
466                         (((uint32) scsiDev.cdb[2]) << 24) +\r
467                         (((uint32) scsiDev.cdb[3]) << 16) +\r
468                         (((uint32) scsiDev.cdb[4]) << 8) +\r
469                         scsiDev.cdb[5];\r
470 \r
471                 doSeek(lba);\r
472         }\r
473         else if (unlikely(command == 0x36))\r
474         {\r
475                 // LOCK UNLOCK CACHE\r
476                 // We don't have a cache to lock data into. do nothing.\r
477         }\r
478         else if (unlikely(command == 0x34))\r
479         {\r
480                 // PRE-FETCH.\r
481                 // We don't have a cache to pre-fetch into. do nothing.\r
482         }\r
483         else if (unlikely(command == 0x1E))\r
484         {\r
485                 // PREVENT ALLOW MEDIUM REMOVAL\r
486                 // Not much we can do to prevent the user removing the SD card.\r
487                 // do nothing.\r
488         }\r
489         else if (unlikely(command == 0x01))\r
490         {\r
491                 // REZERO UNIT\r
492                 // Set the lun to a vendor-specific state. Ignore.\r
493         }\r
494         else if (unlikely(command == 0x35))\r
495         {\r
496                 // SYNCHRONIZE CACHE\r
497                 // We don't have a cache. do nothing.\r
498         }\r
499         else if (unlikely(command == 0x2F))\r
500         {\r
501                 // VERIFY\r
502                 // TODO: When they supply data to verify, we should read the data and\r
503                 // verify it. If they don't supply any data, just say success.\r
504                 if ((scsiDev.cdb[1] & 0x02) == 0)\r
505                 {\r
506                         // They are asking us to do a medium verification with no data\r
507                         // comparison. Assume success, do nothing.\r
508                 }\r
509                 else\r
510                 {\r
511                         // TODO. This means they are supplying data to verify against.\r
512                         // Technically we should probably grab the data and compare it.\r
513                         scsiDev.status = CHECK_CONDITION;\r
514                         scsiDev.target->state.sense.code = ILLEGAL_REQUEST;\r
515                         scsiDev.target->state.sense.asc = INVALID_FIELD_IN_CDB;\r
516                         scsiDev.phase = STATUS;\r
517                 }\r
518         }\r
519         else if (unlikely(command == 0x37))\r
520         {\r
521                 // READ DEFECT DATA\r
522                 uint32_t allocLength = (((uint16_t)scsiDev.cdb[7]) << 8) |\r
523                         scsiDev.cdb[8];\r
524 \r
525                 scsiDev.data[0] = 0;\r
526                 scsiDev.data[1] = scsiDev.cdb[1];\r
527                 scsiDev.data[2] = 0;\r
528                 scsiDev.data[3] = 0;\r
529                 scsiDev.dataLen = 4;\r
530 \r
531                 if (scsiDev.dataLen > allocLength)\r
532                 {\r
533                         scsiDev.dataLen = allocLength;\r
534                 }\r
535 \r
536                 scsiDev.phase = DATA_IN;\r
537         }\r
538         else\r
539         {\r
540                 commandHandled = 0;\r
541         }\r
542 \r
543         return commandHandled;\r
544 }\r
545 \r
546 void scsiDiskPoll()\r
547 {\r
548         uint32_t bytesPerSector = scsiDev.target->state.bytesPerSector;\r
549 \r
550         if (scsiDev.phase == DATA_IN &&\r
551                 transfer.currentBlock != transfer.blocks)\r
552         {\r
553                 scsiEnterPhase(DATA_IN);\r
554 \r
555                 int totalSDSectors =\r
556                         transfer.blocks * SDSectorsPerSCSISector(bytesPerSector);\r
557                 uint32_t sdLBA =\r
558                         SCSISector2SD(\r
559                                 scsiDev.target->cfg->sdSectorStart,\r
560                                 bytesPerSector,\r
561                                 transfer.lba);\r
562 \r
563                 const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector);\r
564                 int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE;\r
565                 int prep = 0;\r
566                 int i = 0;\r
567                 int scsiActive = 0;\r
568                 int sdActive = 0;\r
569         \r
570         int isSDDevice = scsiDev.target->cfg->storageDevice == CONFIG_STOREDEVICE_SD;\r
571         \r
572                 while ((i < totalSDSectors) &&\r
573                         likely(scsiDev.phase == DATA_IN) &&\r
574                         likely(!scsiDev.resetFlag))\r
575                 {\r
576                         // Wait for the next DMA interrupt. It's beneficial to halt the\r
577                         // processor to give the DMA controller more memory bandwidth to\r
578                         // work with.\r
579                         int scsiBusy = 1;\r
580                         int sdBusy = 1;\r
581                         while (scsiBusy && sdBusy)\r
582                         {\r
583                                 uint8_t intr = CyEnterCriticalSection();\r
584                                 scsiBusy = scsiDMABusy();\r
585                                 sdBusy = sdDMABusy();\r
586                                 if (scsiBusy && sdBusy)\r
587                                 {\r
588                                         __WFI();\r
589                                 }\r
590                                 CyExitCriticalSection(intr);\r
591                         }\r
592 \r
593             if (isSDDevice)\r
594             {\r
595                             if (sdActive && !sdBusy && sdReadSectorDMAPoll())\r
596                             {\r
597                                     sdActive = 0;\r
598                                 prep++;\r
599                             }\r
600             }\r
601             else\r
602             {\r
603                 S2S_Device* device = scsiDev.target->device;\r
604                 if (sdActive && device->readAsyncPoll(device))\r
605                             {\r
606                                     sdActive = 0;\r
607                                 prep++;\r
608                             }\r
609             }\r
610 \r
611                         // Usually SD is slower than the SCSI interface.\r
612                         // Prioritise starting the read of the next sector over starting a\r
613                         // SCSI transfer for the last sector\r
614                         // ie. NO "else" HERE.\r
615                         if (!sdActive &&\r
616                                 (prep - i < buffers) &&\r
617                                 (prep < totalSDSectors))\r
618                         {\r
619                 if (isSDDevice)\r
620                 {\r
621                                     // Start an SD transfer if we have space.\r
622                                     if (transfer.multiBlock)\r
623                                     {\r
624                                             sdReadMultiSectorDMA(&scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);\r
625                                     }\r
626                                     else\r
627                                     {\r
628                                             sdReadSingleSectorDMA(sdLBA + prep, &scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);\r
629                                     }\r
630                                     sdActive = 1;\r
631                 }\r
632                 else\r
633                 {\r
634                     // Sync Read onboard flash\r
635                     S2S_Device* device = scsiDev.target->device;\r
636                     device->readAsync(device, sdLBA + prep, 1, &scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);\r
637                     sdActive = 1;\r
638                 }\r
639                         }\r
640 \r
641                         if (scsiActive && !scsiBusy && scsiWriteDMAPoll())\r
642                         {\r
643                                 scsiActive = 0;\r
644                                 ++i;\r
645                         }\r
646                         if (!scsiActive && ((prep - i) > 0))\r
647                         {\r
648                                 int dmaBytes = SD_SECTOR_SIZE;\r
649                                 if ((i % sdPerScsi) == (sdPerScsi - 1))\r
650                                 {\r
651                                         dmaBytes = bytesPerSector % SD_SECTOR_SIZE;\r
652                                         if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE;\r
653                                 }\r
654                                 scsiWriteDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)], dmaBytes);\r
655                                 scsiActive = 1;\r
656                         }\r
657                 }\r
658                 if (scsiDev.phase == DATA_IN)\r
659                 {\r
660                         scsiDev.phase = STATUS;\r
661                 }\r
662                 scsiDiskReset();\r
663         \r
664         // Wait for current DMA transfer done then deselect (if reset encountered)\r
665         if (!isSDDevice)\r
666         {\r
667             S2S_Device* device = scsiDev.target->device;\r
668             while (!device->readAsyncPoll(device))\r
669                     {\r
670                     }\r
671         }\r
672         }\r
673         else if (scsiDev.phase == DATA_OUT &&\r
674                 transfer.currentBlock != transfer.blocks)\r
675         {\r
676                 scsiEnterPhase(DATA_OUT);\r
677 \r
678                 const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector);\r
679                 int totalSDSectors = transfer.blocks * sdPerScsi;\r
680                 int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE;\r
681                 int prep = 0;\r
682                 int i = 0;\r
683                 int scsiDisconnected = 0;\r
684                 int scsiComplete = 0;\r
685                 int clearBSY = 0;\r
686                 uint32_t lastActivityTime = getTime_ms();\r
687                 int scsiActive = 0;\r
688                 int sdActive = 0;\r
689 \r
690                 while ((i < totalSDSectors) &&\r
691                         (likely(scsiDev.phase == DATA_OUT) || // scsiDisconnect keeps our phase.\r
692                                 scsiComplete) &&\r
693                         likely(!scsiDev.resetFlag))\r
694                 {\r
695                         // Wait for the next DMA interrupt. It's beneficial to halt the\r
696                         // processor to give the DMA controller more memory bandwidth to\r
697                         // work with.\r
698                         int scsiBusy = 1;\r
699                         int sdBusy = 1;\r
700                         while (scsiBusy && sdBusy)\r
701                         {\r
702                                 uint8_t intr = CyEnterCriticalSection();\r
703                                 scsiBusy = scsiDMABusy();\r
704                                 sdBusy = sdDMABusy();\r
705                                 if (scsiBusy && sdBusy)\r
706                                 {\r
707                                         __WFI();\r
708                                 }\r
709                                 CyExitCriticalSection(intr);\r
710                         }\r
711 \r
712                         if (sdActive && !sdBusy && sdWriteSectorDMAPoll())\r
713                         {\r
714                                 sdActive = 0;\r
715                                 i++;\r
716                         }\r
717                         if (!sdActive && ((prep - i) > 0))\r
718                         {\r
719                                 // Start an SD transfer if we have space.\r
720                                 sdWriteMultiSectorDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)]);\r
721                                 sdActive = 1;\r
722                         }\r
723 \r
724                         uint32_t now = getTime_ms();\r
725 \r
726                         if (scsiActive && !scsiBusy && scsiReadDMAPoll())\r
727                         {\r
728                                 scsiActive = 0;\r
729                                 ++prep;\r
730                                 lastActivityTime = now;\r
731                         }\r
732                         if (!scsiActive &&\r
733                                 ((prep - i) < buffers) &&\r
734                                 (prep < totalSDSectors) &&\r
735                                 likely(!scsiDisconnected))\r
736                         {\r
737                                 int dmaBytes = SD_SECTOR_SIZE;\r
738                                 if ((prep % sdPerScsi) == (sdPerScsi - 1))\r
739                                 {\r
740                                         dmaBytes = bytesPerSector % SD_SECTOR_SIZE;\r
741                                         if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE;\r
742                                 }\r
743                                 scsiReadDMA(&scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)], dmaBytes);\r
744                                 scsiActive = 1;\r
745                         }\r
746                         else if (\r
747                                 (scsiDev.boardCfg.flags & CONFIG_ENABLE_DISCONNECT) &&\r
748                                 (scsiActive == 0) &&\r
749                                 likely(!scsiDisconnected) &&\r
750                                 unlikely(scsiDev.discPriv) &&\r
751                                 unlikely(diffTime_ms(lastActivityTime, now) >= 20) &&\r
752                                 likely(scsiDev.phase == DATA_OUT))\r
753                         {\r
754                                 // We're transferring over the SCSI bus faster than the SD card\r
755                                 // can write.  There is no more buffer space once we've finished\r
756                                 // this SCSI transfer.\r
757                                 // The NCR 53C700 interface chips have a 250ms "byte-to-byte"\r
758                                 // timeout buffer. SD card writes are supposed to complete\r
759                                 // within 200ms, but sometimes they don't.\r
760                                 // The NCR 53C700 series is used on HP 9000 workstations.\r
761                                 scsiDisconnect();\r
762                                 scsiDisconnected = 1;\r
763                                 lastActivityTime = getTime_ms();\r
764                         }\r
765                         else if (unlikely(scsiDisconnected) &&\r
766                                 (\r
767                                         (prep == i) || // Buffers empty.\r
768                                         // Send some messages every 100ms so we don't timeout.\r
769                                         // At a minimum, a reselection involves an IDENTIFY message.\r
770                                         unlikely(diffTime_ms(lastActivityTime, now) >= 100)\r
771                                 ))\r
772                         {\r
773                                 int reconnected = scsiReconnect();\r
774                                 if (reconnected)\r
775                                 {\r
776                                         scsiDisconnected = 0;\r
777                                         lastActivityTime = getTime_ms(); // Don't disconnect immediately.\r
778                                 }\r
779                                 else if (diffTime_ms(lastActivityTime, getTime_ms()) >= 10000)\r
780                                 {\r
781                                         // Give up after 10 seconds of trying to reconnect.\r
782                                         scsiDev.resetFlag = 1;\r
783                                 }\r
784                         }\r
785                         else if (\r
786                                 likely(!scsiComplete) &&\r
787                                 (sdActive == 1) &&\r
788                                 (prep == totalSDSectors) && // All scsi data read and buffered\r
789                                 likely(!scsiDev.discPriv) && // Prefer disconnect where possible.\r
790                                 unlikely(diffTime_ms(lastActivityTime, now) >= 150) &&\r
791 \r
792                                 likely(scsiDev.phase == DATA_OUT) &&\r
793                                 !(scsiDev.cdb[scsiDev.cdbLen - 1] & 0x01) // Not linked command\r
794                                 )\r
795                         {\r
796                                 // We're transferring over the SCSI bus faster than the SD card\r
797                                 // can write.  All data is buffered, and we're just waiting for\r
798                                 // the SD card to complete. The host won't let us disconnect.\r
799                                 // Some drivers set a 250ms timeout on transfers to complete.\r
800                                 // SD card writes are supposed to complete\r
801                                 // within 200ms, but sometimes they don'to.\r
802                                 // Just pretend we're finished.\r
803                                 scsiComplete = 1;\r
804 \r
805                                 process_Status();\r
806                                 clearBSY = process_MessageIn(0); // Will go to BUS_FREE state but keeps BSY asserted\r
807                         }\r
808                 }\r
809 \r
810                 if (clearBSY)\r
811                 {\r
812                         enter_BusFree();\r
813                 }\r
814                 while (\r
815                         !scsiDev.resetFlag &&\r
816                         unlikely(scsiDisconnected) &&\r
817                         (elapsedTime_ms(lastActivityTime) <= 10000))\r
818                 {\r
819                         scsiDisconnected = !scsiReconnect();\r
820                 }\r
821                 if (scsiDisconnected)\r
822                 {\r
823                         // Failed to reconnect\r
824                         scsiDev.resetFlag = 1;\r
825                 }\r
826 \r
827                 if (scsiDev.phase == DATA_OUT)\r
828                 {\r
829                         if (scsiDev.parityError &&\r
830                                 (scsiDev.boardCfg.flags & CONFIG_ENABLE_PARITY) &&\r
831                                 (scsiDev.compatMode >= COMPAT_SCSI2))\r
832                         {\r
833                                 scsiDev.target->state.sense.code = ABORTED_COMMAND;\r
834                                 scsiDev.target->state.sense.asc = SCSI_PARITY_ERROR;\r
835                                 scsiDev.status = CHECK_CONDITION;;\r
836                         }\r
837                         scsiDev.phase = STATUS;\r
838                 }\r
839                 scsiDiskReset();\r
840         }\r
841 }\r
842 \r
843 void scsiDiskReset()\r
844 {\r
845         scsiDev.dataPtr = 0;\r
846         scsiDev.savedDataPtr = 0;\r
847         scsiDev.dataLen = 0;\r
848         // transfer.lba = 0; // Needed in Request Sense to determine failure\r
849         transfer.blocks = 0;\r
850         transfer.currentBlock = 0;\r
851 \r
852         // Cancel long running commands!\r
853         if (\r
854                 ((scsiDev.boardCfg.flags & CONFIG_ENABLE_CACHE) == 0) ||\r
855                         (transfer.multiBlock == 0)\r
856                 )\r
857         {\r
858                 sdCompleteTransfer();\r
859         }\r
860 \r
861         transfer.multiBlock = 0;\r
862 }\r
863 \r
864 void scsiDiskInit()\r
865 {\r
866         scsiDiskReset();\r
867 \r
868         // WP pin not available for micro-sd\r
869         // TODO read card WP register\r
870         #if 0\r
871         if (SD_WP_Read())\r
872         {\r
873                 blockDev.state = blockDev.state | DISK_WP;\r
874         }\r
875         #endif\r
876 }\r
877 \r
878 \r