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