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