Change default configuration to 2GB limit, no parity, no attention.
[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 "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                 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) scsiDev.cdb[2]) << 24) +\r
119                 (((uint32) scsiDev.cdb[3]) << 16) +\r
120                 (((uint32) 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 lba, uint32 blocks)\r
167 {\r
168         if ((blockDev.state & DISK_WP) ||\r
169                 (scsiDev.target->cfg->deviceType == CONFIG_OPTICAL))\r
170 \r
171         {\r
172                 scsiDev.status = CHECK_CONDITION;\r
173                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
174                 scsiDev.target->sense.asc = WRITE_PROTECTED;\r
175                 scsiDev.phase = STATUS;\r
176         }\r
177         else if (((uint64) lba) + blocks >\r
178                 getScsiCapacity(\r
179                         scsiDev.target->cfg->sdSectorStart,\r
180                         scsiDev.target->liveCfg.bytesPerSector,\r
181                         scsiDev.target->cfg->scsiSectors\r
182                         ))\r
183         {\r
184                 scsiDev.status = CHECK_CONDITION;\r
185                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
186                 scsiDev.target->sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;\r
187                 scsiDev.phase = STATUS;\r
188         }\r
189         else\r
190         {\r
191                 transfer.dir = TRANSFER_WRITE;\r
192                 transfer.lba = lba;\r
193                 transfer.blocks = blocks;\r
194                 transfer.currentBlock = 0;\r
195                 scsiDev.phase = DATA_OUT;\r
196                 scsiDev.dataLen = scsiDev.target->liveCfg.bytesPerSector;\r
197                 scsiDev.dataPtr = scsiDev.target->liveCfg.bytesPerSector;\r
198 \r
199                 // No need for single-block writes atm.  Overhead of the\r
200                 // multi-block write is minimal.\r
201                 transfer.multiBlock = 1;\r
202                 \r
203                 sdWriteMultiSectorPrep();\r
204         }\r
205 }\r
206 \r
207 \r
208 static void doRead(uint32 lba, uint32 blocks)\r
209 {\r
210         uint32_t capacity = getScsiCapacity(\r
211                 scsiDev.target->cfg->sdSectorStart,\r
212                 scsiDev.target->liveCfg.bytesPerSector,\r
213                 scsiDev.target->cfg->scsiSectors);\r
214         if (((uint64) lba) + blocks > capacity)\r
215         {\r
216                 scsiDev.status = CHECK_CONDITION;\r
217                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
218                 scsiDev.target->sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;\r
219                 scsiDev.phase = STATUS;\r
220         }\r
221         else\r
222         {\r
223                 transfer.dir = TRANSFER_READ;\r
224                 transfer.lba = lba;\r
225                 transfer.blocks = blocks;\r
226                 transfer.currentBlock = 0;\r
227                 scsiDev.phase = DATA_IN;\r
228                 scsiDev.dataLen = 0; // No data yet\r
229 \r
230                 if ((blocks == 1) ||\r
231                         (((uint64) lba) + blocks == capacity)\r
232                         )\r
233                 {\r
234                         // We get errors on reading the last sector using a multi-sector\r
235                         // read :-(\r
236                         transfer.multiBlock = 0;\r
237                 }\r
238                 else\r
239                 {\r
240                         transfer.multiBlock = 1;\r
241                         sdReadMultiSectorPrep();\r
242                 }\r
243         }\r
244 }\r
245 \r
246 static void doSeek(uint32 lba)\r
247 {\r
248         if (lba >=\r
249                 getScsiCapacity(\r
250                         scsiDev.target->cfg->sdSectorStart,\r
251                         scsiDev.target->liveCfg.bytesPerSector,\r
252                         scsiDev.target->cfg->scsiSectors)\r
253                 )\r
254         {\r
255                 scsiDev.status = CHECK_CONDITION;\r
256                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
257                 scsiDev.target->sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;\r
258                 scsiDev.phase = STATUS;\r
259         }\r
260 }\r
261 \r
262 static int doTestUnitReady()\r
263 {\r
264         int ready = 1;\r
265         if (!(blockDev.state & DISK_STARTED))\r
266         {\r
267                 ready = 0;\r
268                 scsiDev.status = CHECK_CONDITION;\r
269                 scsiDev.target->sense.code = NOT_READY;\r
270                 scsiDev.target->sense.asc = LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED;\r
271                 scsiDev.phase = STATUS;\r
272         }\r
273         else if (!(blockDev.state & DISK_PRESENT))\r
274         {\r
275                 ready = 0;\r
276                 scsiDev.status = CHECK_CONDITION;\r
277                 scsiDev.target->sense.code = NOT_READY;\r
278                 scsiDev.target->sense.asc = MEDIUM_NOT_PRESENT;\r
279                 scsiDev.phase = STATUS;\r
280         }\r
281         else if (!(blockDev.state & DISK_INITIALISED))\r
282         {\r
283                 ready = 0;\r
284                 scsiDev.status = CHECK_CONDITION;\r
285                 scsiDev.target->sense.code = NOT_READY;\r
286                 scsiDev.target->sense.asc = LOGICAL_UNIT_NOT_READY_CAUSE_NOT_REPORTABLE;\r
287                 scsiDev.phase = STATUS;\r
288         }\r
289         return ready;\r
290 }\r
291 \r
292 // Handle direct-access scsi device commands\r
293 int scsiDiskCommand()\r
294 {\r
295         int commandHandled = 1;\r
296 \r
297         uint8 command = scsiDev.cdb[0];\r
298         if (command == 0x1B)\r
299         {\r
300                 // START STOP UNIT\r
301                 // Enable or disable media access operations.\r
302                 // Ignore load/eject requests. We can't do that.\r
303                 //int immed = scsiDev.cdb[1] & 1;\r
304                 int start = scsiDev.cdb[4] & 1;\r
305 \r
306                 if (start)\r
307                 {\r
308                         blockDev.state = blockDev.state | DISK_STARTED;\r
309                         if (!(blockDev.state & DISK_INITIALISED))\r
310                         {\r
311                                 doSdInit();\r
312                         }\r
313                 }\r
314                 else\r
315                 {\r
316                         blockDev.state &= ~DISK_STARTED;\r
317                 }\r
318         }\r
319         else if (command == 0x00)\r
320         {\r
321                 // TEST UNIT READY\r
322                 doTestUnitReady();\r
323         }\r
324         else if (!doTestUnitReady())\r
325         {\r
326                 // Status and sense codes already set by doTestUnitReady\r
327         }\r
328         else if (command == 0x04)\r
329         {\r
330                 // FORMAT UNIT\r
331                 // We don't really do any formatting, but we need to read the correct\r
332                 // number of bytes in the DATA_OUT phase to make the SCSI host happy.\r
333 \r
334                 int fmtData = (scsiDev.cdb[1] & 0x10) ? 1 : 0;\r
335                 if (fmtData)\r
336                 {\r
337                         // We need to read the parameter list, but we don't know how\r
338                         // big it is yet. Start with the header.\r
339                         scsiDev.dataLen = 4;\r
340                         scsiDev.phase = DATA_OUT;\r
341                         scsiDev.postDataOutHook = doFormatUnitHeader;\r
342                 }\r
343                 else\r
344                 {\r
345                         // No data to read, we're already finished!\r
346                 }\r
347         }\r
348         else if (command == 0x08)\r
349         {\r
350                 // READ(6)\r
351                 uint32 lba =\r
352                         (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +\r
353                         (((uint32) scsiDev.cdb[2]) << 8) +\r
354                         scsiDev.cdb[3];\r
355                 uint32 blocks = scsiDev.cdb[4];\r
356                 if (blocks == 0) blocks = 256;\r
357                 doRead(lba, blocks);\r
358         }\r
359 \r
360         else if (command == 0x28)\r
361         {\r
362                 // READ(10)\r
363                 // Ignore all cache control bits - we don't support a memory cache.\r
364 \r
365                 uint32 lba =\r
366                         (((uint32) scsiDev.cdb[2]) << 24) +\r
367                         (((uint32) scsiDev.cdb[3]) << 16) +\r
368                         (((uint32) scsiDev.cdb[4]) << 8) +\r
369                         scsiDev.cdb[5];\r
370                 uint32 blocks =\r
371                         (((uint32) scsiDev.cdb[7]) << 8) +\r
372                         scsiDev.cdb[8];\r
373 \r
374                 doRead(lba, blocks);\r
375         }\r
376 \r
377         else if (command == 0x25)\r
378         {\r
379                 // READ CAPACITY\r
380                 doReadCapacity();\r
381         }\r
382 \r
383         else if (command == 0x0B)\r
384         {\r
385                 // SEEK(6)\r
386                 uint32 lba =\r
387                         (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +\r
388                         (((uint32) scsiDev.cdb[2]) << 8) +\r
389                         scsiDev.cdb[3];\r
390 \r
391                 doSeek(lba);\r
392         }\r
393 \r
394         else if (command == 0x2B)\r
395         {\r
396                 // SEEK(10)\r
397                 uint32 lba =\r
398                         (((uint32) scsiDev.cdb[2]) << 24) +\r
399                         (((uint32) scsiDev.cdb[3]) << 16) +\r
400                         (((uint32) scsiDev.cdb[4]) << 8) +\r
401                         scsiDev.cdb[5];\r
402 \r
403                 doSeek(lba);\r
404         }\r
405         else if (command == 0x0A)\r
406         {\r
407                 // WRITE(6)\r
408                 uint32 lba =\r
409                         (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +\r
410                         (((uint32) scsiDev.cdb[2]) << 8) +\r
411                         scsiDev.cdb[3];\r
412                 uint32 blocks = scsiDev.cdb[4];\r
413                 if (blocks == 0) blocks = 256;\r
414                 doWrite(lba, blocks);\r
415         }\r
416 \r
417         else if (command == 0x2A)\r
418         {\r
419                 // WRITE(10)\r
420                 // Ignore all cache control bits - we don't support a memory cache.\r
421 \r
422                 uint32 lba =\r
423                         (((uint32) scsiDev.cdb[2]) << 24) +\r
424                         (((uint32) scsiDev.cdb[3]) << 16) +\r
425                         (((uint32) scsiDev.cdb[4]) << 8) +\r
426                         scsiDev.cdb[5];\r
427                 uint32 blocks =\r
428                         (((uint32) scsiDev.cdb[7]) << 8) +\r
429                         scsiDev.cdb[8];\r
430 \r
431                 doWrite(lba, blocks);\r
432         }\r
433         else if (command == 0x36)\r
434         {\r
435                 // LOCK UNLOCK CACHE\r
436                 // We don't have a cache to lock data into. do nothing.\r
437         }\r
438         else if (command == 0x34)\r
439         {\r
440                 // PRE-FETCH.\r
441                 // We don't have a cache to pre-fetch into. do nothing.\r
442         }\r
443         else if (command == 0x1E)\r
444         {\r
445                 // PREVENT ALLOW MEDIUM REMOVAL\r
446                 // Not much we can do to prevent the user removing the SD card.\r
447                 // do nothing.\r
448         }\r
449         else if (command == 0x01)\r
450         {\r
451                 // REZERO UNIT\r
452                 // Set the lun to a vendor-specific state. Ignore.\r
453         }\r
454         else if (command == 0x35)\r
455         {\r
456                 // SYNCHRONIZE CACHE\r
457                 // We don't have a cache. do nothing.\r
458         }\r
459         else if (command == 0x2F)\r
460         {\r
461                 // VERIFY\r
462                 // TODO: When they supply data to verify, we should read the data and\r
463                 // verify it. If they don't supply any data, just say success.\r
464                 if ((scsiDev.cdb[1] & 0x02) == 0)\r
465                 {\r
466                         // They are asking us to do a medium verification with no data\r
467                         // comparison. Assume success, do nothing.\r
468                 }\r
469                 else\r
470                 {\r
471                         // TODO. This means they are supplying data to verify against.\r
472                         // Technically we should probably grab the data and compare it.\r
473                         scsiDev.status = CHECK_CONDITION;\r
474                         scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
475                         scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
476                         scsiDev.phase = STATUS;\r
477                 }\r
478         }\r
479         else\r
480         {\r
481                 commandHandled = 0;\r
482         }\r
483 \r
484         return commandHandled;\r
485 }\r
486 \r
487 void scsiDiskPoll()\r
488 {\r
489         if (scsiDev.phase == DATA_IN &&\r
490                 transfer.currentBlock != transfer.blocks)\r
491         {\r
492                 scsiEnterPhase(DATA_IN);\r
493 \r
494                 int totalSDSectors =\r
495                         transfer.blocks *\r
496                                 SDSectorsPerSCSISector(scsiDev.target->liveCfg.bytesPerSector);\r
497                 uint32_t sdLBA =\r
498                         SCSISector2SD(\r
499                                 scsiDev.target->cfg->sdSectorStart,\r
500                                 scsiDev.target->liveCfg.bytesPerSector,\r
501                                 transfer.lba);\r
502 \r
503                 const int sdPerScsi =\r
504                         SDSectorsPerSCSISector(scsiDev.target->liveCfg.bytesPerSector);\r
505                 int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE;\r
506                 int prep = 0;\r
507                 int i = 0;\r
508                 int scsiActive = 0;\r
509                 int sdActive = 0;\r
510                 while ((i < totalSDSectors) &&\r
511                         (scsiDev.phase == DATA_IN) &&\r
512                         !scsiDev.resetFlag)\r
513                 {\r
514                         if ((sdActive == 1) && sdReadSectorDMAPoll())\r
515                         {\r
516                                 sdActive = 0;\r
517                                 prep++;\r
518                         }\r
519                         else if ((sdActive == 0) && (prep - i < buffers) && (prep < totalSDSectors))\r
520                         {\r
521                                 // Start an SD transfer if we have space.\r
522                                 if (transfer.multiBlock)\r
523                                 {\r
524                                         sdReadMultiSectorDMA(&scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);\r
525                                 }\r
526                                 else\r
527                                 {\r
528                                         sdReadSingleSectorDMA(sdLBA + prep, &scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);\r
529                                 }\r
530                                 sdActive = 1;\r
531                         }\r
532 \r
533                         if ((scsiActive == 1) && scsiWriteDMAPoll())\r
534                         {\r
535                                 scsiActive = 0;\r
536                                 ++i;\r
537                         }\r
538                         else if ((scsiActive == 0) && ((prep - i) > 0))\r
539                         {\r
540                                 int dmaBytes = SD_SECTOR_SIZE;\r
541                                 if ((i % sdPerScsi) == (sdPerScsi - 1))\r
542                                 {\r
543                                         dmaBytes = scsiDev.target->liveCfg.bytesPerSector % SD_SECTOR_SIZE;\r
544                                         if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE;\r
545                                 }\r
546                                 scsiWriteDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)], dmaBytes);\r
547                                 scsiActive = 1;\r
548                         }\r
549                 }\r
550                 if (scsiDev.phase == DATA_IN)\r
551                 {\r
552                         scsiDev.phase = STATUS;\r
553                 }\r
554                 scsiDiskReset();\r
555         }\r
556         else if (scsiDev.phase == DATA_OUT &&\r
557                 transfer.currentBlock != transfer.blocks)\r
558         {\r
559                 scsiEnterPhase(DATA_OUT);\r
560 \r
561                 const int sdPerScsi =\r
562                         SDSectorsPerSCSISector(scsiDev.target->liveCfg.bytesPerSector);\r
563                 int totalSDSectors = transfer.blocks * sdPerScsi;\r
564                 int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE;\r
565                 int prep = 0;\r
566                 int i = 0;\r
567                 int scsiDisconnected = 0;\r
568                 volatile uint32_t lastActivityTime = getTime_ms();\r
569                 int scsiActive = 0;\r
570                 int sdActive = 0;\r
571                 \r
572                 while ((i < totalSDSectors) &&\r
573                         (scsiDev.phase == DATA_OUT) && // scsiDisconnect keeps our phase.\r
574                         !scsiDev.resetFlag)\r
575                 {\r
576                         if ((sdActive == 1) && sdWriteSectorDMAPoll())\r
577                         {\r
578                                 sdActive = 0;\r
579                                 i++;\r
580                         }\r
581                         else if ((sdActive == 0) && ((prep - i) > 0))\r
582                         {\r
583                                 // Start an SD transfer if we have space.\r
584                                 sdWriteMultiSectorDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)]);\r
585                                 sdActive = 1;\r
586                         }\r
587 \r
588                         if ((scsiActive == 1) && scsiReadDMAPoll())\r
589                         {\r
590                                 scsiActive = 0;\r
591                                 ++prep;\r
592                                 lastActivityTime = getTime_ms();\r
593                         }\r
594                         else if ((scsiActive == 0) &&\r
595                                 ((prep - i) < buffers) &&\r
596                                 (prep < totalSDSectors) &&\r
597                                 !scsiDisconnected)\r
598                         {\r
599                                 int dmaBytes = SD_SECTOR_SIZE;\r
600                                 if ((prep % sdPerScsi) == (sdPerScsi - 1))\r
601                                 {\r
602                                         dmaBytes = scsiDev.target->liveCfg.bytesPerSector % SD_SECTOR_SIZE;\r
603                                         if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE;\r
604                                 }\r
605                                 scsiReadDMA(&scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)], dmaBytes);\r
606                                 scsiActive = 1;\r
607                         }\r
608                         else if (\r
609                                 (scsiActive == 0) &&\r
610                                 !scsiDisconnected &&\r
611                                 scsiDev.discPriv &&\r
612                                 (diffTime_ms(lastActivityTime, getTime_ms()) >= 20) &&\r
613                                 (scsiDev.phase == DATA_OUT))\r
614                         {\r
615                                 // We're transferring over the SCSI bus faster than the SD card\r
616                                 // can write.  There is no more buffer space once we've finished\r
617                                 // this SCSI transfer.\r
618                                 // The NCR 53C700 interface chips have a 250ms "byte-to-byte"\r
619                                 // timeout buffer. SD card writes are supposed to complete\r
620                                 // within 200ms, but sometimes they don't.\r
621                                 // The NCR 53C700 series is used on HP 9000 workstations.\r
622                                 scsiDisconnect();\r
623                                 scsiDisconnected = 1;\r
624                                 lastActivityTime = getTime_ms();\r
625                         }\r
626                         else if (scsiDisconnected &&\r
627                                 (\r
628                                         (prep == i) || // Buffers empty.\r
629                                         // Send some messages every 100ms so we don't timeout.\r
630                                         // At a minimum, a reselection involves an IDENTIFY message.\r
631                                         (diffTime_ms(lastActivityTime, getTime_ms()) >= 100)\r
632                                 ))\r
633                         {\r
634                                 int reconnected = scsiReconnect();\r
635                                 if (reconnected)\r
636                                 {\r
637                                         scsiDisconnected = 0;\r
638                                         lastActivityTime = getTime_ms(); // Don't disconnect immediately.\r
639                                 }\r
640                                 else if (diffTime_ms(lastActivityTime, getTime_ms()) >= 10000)\r
641                                 {\r
642                                         // Give up after 10 seconds of trying to reconnect.\r
643                                         scsiDev.resetFlag = 1;\r
644                                 }\r
645                         }\r
646                 }\r
647 \r
648                 while (\r
649                         !scsiDev.resetFlag &&\r
650                         scsiDisconnected &&\r
651                         (diffTime_ms(lastActivityTime, getTime_ms()) <= 10000))\r
652                 {\r
653                         scsiDisconnected = !scsiReconnect();\r
654                 }\r
655                 if (scsiDisconnected)\r
656                 {\r
657                         // Failed to reconnect\r
658                         scsiDev.resetFlag = 1;\r
659                 }\r
660 \r
661                 if (scsiDev.phase == DATA_OUT)\r
662                 {\r
663                         if (scsiDev.parityError &&\r
664                                 (scsiDev.target->cfg->flags & CONFIG_ENABLE_PARITY) &&\r
665                                 !scsiDev.compatMode)\r
666                         {\r
667                                 scsiDev.target->sense.code = ABORTED_COMMAND;\r
668                                 scsiDev.target->sense.asc = SCSI_PARITY_ERROR;\r
669                                 scsiDev.status = CHECK_CONDITION;;\r
670                         }\r
671                         scsiDev.phase = STATUS;\r
672                 }\r
673                 scsiDiskReset();\r
674         }\r
675 }\r
676 \r
677 void scsiDiskReset()\r
678 {\r
679         scsiDev.dataPtr = 0;\r
680         scsiDev.savedDataPtr = 0;\r
681         scsiDev.dataLen = 0;\r
682         // transfer.lba = 0; // Needed in Request Sense to determine failure\r
683         transfer.blocks = 0;\r
684         transfer.currentBlock = 0;\r
685 \r
686         // Cancel long running commands!\r
687         if (transfer.inProgress == 1)\r
688         {\r
689                 if (transfer.dir == TRANSFER_WRITE)\r
690                 {\r
691                         sdCompleteWrite();\r
692                 }\r
693                 else\r
694                 {\r
695                         sdCompleteRead();\r
696                 }\r
697         }\r
698         transfer.inProgress = 0;\r
699         transfer.multiBlock = 0;\r
700 }\r
701 \r
702 void scsiDiskInit()\r
703 {\r
704         transfer.inProgress = 0;\r
705         scsiDiskReset();\r
706 \r
707         // Don't require the host to send us a START STOP UNIT command\r
708         blockDev.state = DISK_STARTED;\r
709         // WP pin not available for micro-sd\r
710         // TODO read card WP register\r
711         #if 0\r
712         if (SD_WP_Read())\r
713         {\r
714                 blockDev.state = blockDev.state | DISK_WP;\r
715         }\r
716         #endif\r
717 }\r
718 \r