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