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