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