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;\r
580                         int sdBusy;\r
581                         {\r
582                                 uint8_t intr = CyEnterCriticalSection();\r
583                                 scsiBusy = scsiDMABusy();\r
584                                 sdBusy = isSDDevice && sdDMABusy();\r
585                                 CyExitCriticalSection(intr);\r
586                         }\r
587                         while (scsiBusy && sdBusy && isSDDevice)\r
588                         {\r
589                                 uint8_t intr = CyEnterCriticalSection();\r
590                                 scsiBusy = scsiDMABusy();\r
591                                 sdBusy = sdDMABusy();\r
592                                 if (scsiBusy && sdBusy)\r
593                                 {\r
594                                         __WFI();\r
595                                 }\r
596                                 CyExitCriticalSection(intr);\r
597                         }\r
598 \r
599             if (isSDDevice)\r
600             {\r
601                             if (sdActive && !sdBusy && sdReadSectorDMAPoll())\r
602                             {\r
603                                     sdActive = 0;\r
604                                 prep++;\r
605                             }\r
606             }\r
607             else\r
608             {\r
609                 S2S_Device* device = scsiDev.target->device;\r
610                 if (sdActive && device->readAsyncPoll(device))\r
611                             {\r
612                                     sdActive = 0;\r
613                                 prep++;\r
614                             }\r
615             }\r
616 \r
617                         // Usually SD is slower than the SCSI interface.\r
618                         // Prioritise starting the read of the next sector over starting a\r
619                         // SCSI transfer for the last sector\r
620                         // ie. NO "else" HERE.\r
621                         if (!sdActive &&\r
622                                 (prep - i < buffers) &&\r
623                                 (prep < totalSDSectors))\r
624                         {\r
625                 if (isSDDevice)\r
626                 {\r
627                                     // Start an SD transfer if we have space.\r
628                                     if (transfer.multiBlock)\r
629                                     {\r
630                                             sdReadMultiSectorDMA(&scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);\r
631                                     }\r
632                                     else\r
633                                     {\r
634                                             sdReadSingleSectorDMA(sdLBA + prep, &scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);\r
635                                     }\r
636                                     sdActive = 1;\r
637                 }\r
638                 else\r
639                 {\r
640                     // Sync Read onboard flash\r
641                     S2S_Device* device = scsiDev.target->device;\r
642                     device->readAsync(device, sdLBA + prep, 1, &scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);\r
643                     sdActive = 1;\r
644                 }\r
645                         }\r
646 \r
647                         if (scsiActive && !scsiBusy && scsiWriteDMAPoll())\r
648                         {\r
649                                 scsiActive = 0;\r
650                                 ++i;\r
651                         }\r
652                         if (!scsiActive && ((prep - i) > 0))\r
653                         {\r
654                                 int dmaBytes = SD_SECTOR_SIZE;\r
655                                 if ((i % sdPerScsi) == (sdPerScsi - 1))\r
656                                 {\r
657                                         dmaBytes = bytesPerSector % SD_SECTOR_SIZE;\r
658                                         if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE;\r
659                                 }\r
660                                 scsiWriteDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)], dmaBytes);\r
661                                 scsiActive = 1;\r
662                         }\r
663                 }\r
664                 if (scsiDev.phase == DATA_IN)\r
665                 {\r
666                         scsiDev.phase = STATUS;\r
667                 }\r
668                 scsiDiskReset();\r
669         \r
670         // Wait for current DMA transfer done then deselect (if reset encountered)\r
671         if (!isSDDevice)\r
672         {\r
673             S2S_Device* device = scsiDev.target->device;\r
674             while (!device->readAsyncPoll(device))\r
675                     {\r
676                     }\r
677         }\r
678         }\r
679         else if (scsiDev.phase == DATA_OUT &&\r
680                 transfer.currentBlock != transfer.blocks)\r
681         {\r
682                 scsiEnterPhase(DATA_OUT);\r
683 \r
684                 const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector);\r
685                 int totalSDSectors = transfer.blocks * sdPerScsi;\r
686                 int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE;\r
687                 int prep = 0;\r
688                 int i = 0;\r
689                 int scsiDisconnected = 0;\r
690                 int scsiComplete = 0;\r
691                 int clearBSY = 0;\r
692                 uint32_t lastActivityTime = getTime_ms();\r
693                 int scsiActive = 0;\r
694                 int sdActive = 0;\r
695 \r
696                 while ((i < totalSDSectors) &&\r
697                         (likely(scsiDev.phase == DATA_OUT) || // scsiDisconnect keeps our phase.\r
698                                 scsiComplete) &&\r
699                         likely(!scsiDev.resetFlag))\r
700                 {\r
701                         // Wait for the next DMA interrupt. It's beneficial to halt the\r
702                         // processor to give the DMA controller more memory bandwidth to\r
703                         // work with.\r
704                         int scsiBusy = 1;\r
705                         int sdBusy = 1;\r
706                         while (scsiBusy && sdBusy)\r
707                         {\r
708                                 uint8_t intr = CyEnterCriticalSection();\r
709                                 scsiBusy = scsiDMABusy();\r
710                                 sdBusy = sdDMABusy();\r
711                                 if (scsiBusy && sdBusy)\r
712                                 {\r
713                                         __WFI();\r
714                                 }\r
715                                 CyExitCriticalSection(intr);\r
716                         }\r
717 \r
718                         if (sdActive && !sdBusy && sdWriteSectorDMAPoll())\r
719                         {\r
720                                 sdActive = 0;\r
721                                 i++;\r
722                         }\r
723                         if (!sdActive && ((prep - i) > 0))\r
724                         {\r
725                                 // Start an SD transfer if we have space.\r
726                                 sdWriteMultiSectorDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)]);\r
727                                 sdActive = 1;\r
728                         }\r
729 \r
730                         uint32_t now = getTime_ms();\r
731 \r
732                         if (scsiActive && !scsiBusy && scsiReadDMAPoll())\r
733                         {\r
734                                 scsiActive = 0;\r
735                                 ++prep;\r
736                                 lastActivityTime = now;\r
737                         }\r
738                         if (!scsiActive &&\r
739                                 ((prep - i) < buffers) &&\r
740                                 (prep < totalSDSectors) &&\r
741                                 likely(!scsiDisconnected))\r
742                         {\r
743                                 int dmaBytes = SD_SECTOR_SIZE;\r
744                                 if ((prep % sdPerScsi) == (sdPerScsi - 1))\r
745                                 {\r
746                                         dmaBytes = bytesPerSector % SD_SECTOR_SIZE;\r
747                                         if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE;\r
748                                 }\r
749                                 scsiReadDMA(&scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)], dmaBytes);\r
750                                 scsiActive = 1;\r
751                         }\r
752                         else if (\r
753                                 (scsiDev.boardCfg.flags & CONFIG_ENABLE_DISCONNECT) &&\r
754                                 (scsiActive == 0) &&\r
755                                 likely(!scsiDisconnected) &&\r
756                                 unlikely(scsiDev.discPriv) &&\r
757                                 unlikely(diffTime_ms(lastActivityTime, now) >= 20) &&\r
758                                 likely(scsiDev.phase == DATA_OUT))\r
759                         {\r
760                                 // We're transferring over the SCSI bus faster than the SD card\r
761                                 // can write.  There is no more buffer space once we've finished\r
762                                 // this SCSI transfer.\r
763                                 // The NCR 53C700 interface chips have a 250ms "byte-to-byte"\r
764                                 // timeout buffer. SD card writes are supposed to complete\r
765                                 // within 200ms, but sometimes they don't.\r
766                                 // The NCR 53C700 series is used on HP 9000 workstations.\r
767                                 scsiDisconnect();\r
768                                 scsiDisconnected = 1;\r
769                                 lastActivityTime = getTime_ms();\r
770                         }\r
771                         else if (unlikely(scsiDisconnected) &&\r
772                                 (\r
773                                         (prep == i) || // Buffers empty.\r
774                                         // Send some messages every 100ms so we don't timeout.\r
775                                         // At a minimum, a reselection involves an IDENTIFY message.\r
776                                         unlikely(diffTime_ms(lastActivityTime, now) >= 100)\r
777                                 ))\r
778                         {\r
779                                 int reconnected = scsiReconnect();\r
780                                 if (reconnected)\r
781                                 {\r
782                                         scsiDisconnected = 0;\r
783                                         lastActivityTime = getTime_ms(); // Don't disconnect immediately.\r
784                                 }\r
785                                 else if (diffTime_ms(lastActivityTime, getTime_ms()) >= 10000)\r
786                                 {\r
787                                         // Give up after 10 seconds of trying to reconnect.\r
788                                         scsiDev.resetFlag = 1;\r
789                                 }\r
790                         }\r
791                         else if (\r
792                                 likely(!scsiComplete) &&\r
793                                 (sdActive == 1) &&\r
794                                 (prep == totalSDSectors) && // All scsi data read and buffered\r
795                                 likely(!scsiDev.discPriv) && // Prefer disconnect where possible.\r
796                                 unlikely(diffTime_ms(lastActivityTime, now) >= 150) &&\r
797 \r
798                                 likely(scsiDev.phase == DATA_OUT) &&\r
799                                 !(scsiDev.cdb[scsiDev.cdbLen - 1] & 0x01) // Not linked command\r
800                                 )\r
801                         {\r
802                                 // We're transferring over the SCSI bus faster than the SD card\r
803                                 // can write.  All data is buffered, and we're just waiting for\r
804                                 // the SD card to complete. The host won't let us disconnect.\r
805                                 // Some drivers set a 250ms timeout on transfers to complete.\r
806                                 // SD card writes are supposed to complete\r
807                                 // within 200ms, but sometimes they don'to.\r
808                                 // Just pretend we're finished.\r
809                                 scsiComplete = 1;\r
810 \r
811                                 process_Status();\r
812                                 clearBSY = process_MessageIn(0); // Will go to BUS_FREE state but keeps BSY asserted\r
813                         }\r
814                 }\r
815 \r
816                 if (clearBSY)\r
817                 {\r
818                         enter_BusFree();\r
819                 }\r
820                 while (\r
821                         !scsiDev.resetFlag &&\r
822                         unlikely(scsiDisconnected) &&\r
823                         (elapsedTime_ms(lastActivityTime) <= 10000))\r
824                 {\r
825                         scsiDisconnected = !scsiReconnect();\r
826                 }\r
827                 if (scsiDisconnected)\r
828                 {\r
829                         // Failed to reconnect\r
830                         scsiDev.resetFlag = 1;\r
831                 }\r
832 \r
833                 if (scsiDev.phase == DATA_OUT)\r
834                 {\r
835                         if (scsiDev.parityError &&\r
836                                 (scsiDev.boardCfg.flags & CONFIG_ENABLE_PARITY) &&\r
837                                 (scsiDev.compatMode >= COMPAT_SCSI2))\r
838                         {\r
839                                 scsiDev.target->state.sense.code = ABORTED_COMMAND;\r
840                                 scsiDev.target->state.sense.asc = SCSI_PARITY_ERROR;\r
841                                 scsiDev.status = CHECK_CONDITION;;\r
842                         }\r
843                         scsiDev.phase = STATUS;\r
844                 }\r
845                 scsiDiskReset();\r
846         }\r
847 }\r
848 \r
849 void scsiDiskReset()\r
850 {\r
851         scsiDev.dataPtr = 0;\r
852         scsiDev.savedDataPtr = 0;\r
853         scsiDev.dataLen = 0;\r
854         // transfer.lba = 0; // Needed in Request Sense to determine failure\r
855         transfer.blocks = 0;\r
856         transfer.currentBlock = 0;\r
857 \r
858         // Cancel long running commands!\r
859         if (\r
860                 ((scsiDev.boardCfg.flags & CONFIG_ENABLE_CACHE) == 0) ||\r
861                         (transfer.multiBlock == 0)\r
862                 )\r
863         {\r
864                 sdCompleteTransfer();\r
865         }\r
866 \r
867         transfer.multiBlock = 0;\r
868 }\r
869 \r
870 void scsiDiskInit()\r
871 {\r
872         scsiDiskReset();\r
873 \r
874         // WP pin not available for micro-sd\r
875         // TODO read card WP register\r
876         #if 0\r
877         if (SD_WP_Read())\r
878         {\r
879                 blockDev.state = blockDev.state | DISK_WP;\r
880         }\r
881         #endif\r
882 }\r
883 \r
884 \r