Commit | Line | Data |
---|---|---|
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 | |
34 | BlockDevice blockDev;\r | |
35 | Transfer transfer;\r | |
36 | \r | |
27e482ea MM |
37 | static 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 |
53 | static 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 | |
60 | static 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 | |
74 | static 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 | |
89 | static 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 | |
120 | static 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 | |
170 | static 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 | |
213 | static 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 | |
251 | static 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 | |
267 | static 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 | |
302 | int 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 |
495 | void 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 | |
744 | void 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 | |
769 | void 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 |