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