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