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