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