1 // Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
\r
2 // Copyright (C) 2014 Doug Brown <doug@downtowndougbrown.com>
\r
4 // This file is part of SCSI2SD.
\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
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
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
21 #include "scsiPhy.h"
\r
29 BlockDevice blockDev;
\r
32 static int doSdInit()
\r
34 int result = sdInit();
\r
37 blockDev.state = blockDev.state | DISK_INITIALISED;
\r
42 // Callback once all data has been read in the data out phase.
\r
43 static void doFormatUnitComplete(void)
\r
45 // TODO start writing the initialisation pattern to the SD
\r
47 scsiDev.phase = STATUS;
\r
50 static void doFormatUnitSkipData(int bytes)
\r
52 // We may not have enough memory to store the initialisation pattern and
\r
53 // defect list data. Since we're not making use of it yet anyway, just
\r
54 // discard the bytes.
\r
55 scsiEnterPhase(DATA_OUT);
\r
57 for (i = 0; i < bytes; ++i)
\r
63 // Callback from the data out phase.
\r
64 static void doFormatUnitPatternHeader(void)
\r
67 ((((uint16_t)scsiDev.data[2])) << 8) +
\r
71 ((((uint16_t)scsiDev.data[4 + 2])) << 8) +
\r
72 scsiDev.data[4 + 3];
\r
74 doFormatUnitSkipData(defectLength + patternLength);
\r
75 doFormatUnitComplete();
\r
78 // Callback from the data out phase.
\r
79 static void doFormatUnitHeader(void)
\r
81 int IP = (scsiDev.data[1] & 0x08) ? 1 : 0;
\r
82 int DSP = (scsiDev.data[1] & 0x04) ? 1 : 0;
\r
84 if (! DSP) // disable save parameters
\r
86 configSave(); // Save the "MODE SELECT savable parameters"
\r
91 // We need to read the initialisation pattern header first.
\r
92 scsiDev.dataLen += 4;
\r
93 scsiDev.phase = DATA_OUT;
\r
94 scsiDev.postDataOutHook = doFormatUnitPatternHeader;
\r
98 // Read the defect list data
\r
100 ((((uint16_t)scsiDev.data[2])) << 8) +
\r
102 doFormatUnitSkipData(defectLength);
\r
103 doFormatUnitComplete();
\r
107 static void doReadCapacity()
\r
109 uint32_t lba = (((uint32) scsiDev.cdb[2]) << 24) +
\r
110 (((uint32) scsiDev.cdb[3]) << 16) +
\r
111 (((uint32) scsiDev.cdb[4]) << 8) +
\r
113 int pmi = scsiDev.cdb[8] & 1;
\r
115 uint32_t capacity = getScsiCapacity();
\r
120 // We don't do anything with the "partial medium indicator", and
\r
121 // assume that delays are constant across each block. But the spec
\r
122 // says we must return this error if pmi is specified incorrectly.
\r
123 scsiDev.status = CHECK_CONDITION;
\r
124 scsiDev.sense.code = ILLEGAL_REQUEST;
\r
125 scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
\r
126 scsiDev.phase = STATUS;
\r
128 else if (capacity > 0)
\r
130 uint32_t highestBlock = capacity - 1;
\r
132 scsiDev.data[0] = highestBlock >> 24;
\r
133 scsiDev.data[1] = highestBlock >> 16;
\r
134 scsiDev.data[2] = highestBlock >> 8;
\r
135 scsiDev.data[3] = highestBlock;
\r
137 scsiDev.data[4] = config->bytesPerSector >> 24;
\r
138 scsiDev.data[5] = config->bytesPerSector >> 16;
\r
139 scsiDev.data[6] = config->bytesPerSector >> 8;
\r
140 scsiDev.data[7] = config->bytesPerSector;
\r
141 scsiDev.dataLen = 8;
\r
142 scsiDev.phase = DATA_IN;
\r
146 scsiDev.status = CHECK_CONDITION;
\r
147 scsiDev.sense.code = NOT_READY;
\r
148 scsiDev.sense.asc = MEDIUM_NOT_PRESENT;
\r
149 scsiDev.phase = STATUS;
\r
153 static void doWrite(uint32 lba, uint32 blocks)
\r
155 if (blockDev.state & DISK_WP)
\r
157 scsiDev.status = CHECK_CONDITION;
\r
158 scsiDev.sense.code = ILLEGAL_REQUEST;
\r
159 scsiDev.sense.asc = WRITE_PROTECTED;
\r
160 scsiDev.phase = STATUS;
\r
162 else if (((uint64) lba) + blocks > getScsiCapacity())
\r
164 scsiDev.status = CHECK_CONDITION;
\r
165 scsiDev.sense.code = ILLEGAL_REQUEST;
\r
166 scsiDev.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
\r
167 scsiDev.phase = STATUS;
\r
171 transfer.dir = TRANSFER_WRITE;
\r
172 transfer.lba = lba;
\r
173 transfer.blocks = blocks;
\r
174 transfer.currentBlock = 0;
\r
175 scsiDev.phase = DATA_OUT;
\r
176 scsiDev.dataLen = config->bytesPerSector;
\r
177 scsiDev.dataPtr = config->bytesPerSector; // TODO FIX scsiDiskPoll()
\r
179 // No need for single-block writes atm. Overhead of the
\r
180 // multi-block write is minimal.
\r
181 transfer.multiBlock = 1;
\r
187 static void doRead(uint32 lba, uint32 blocks)
\r
189 uint32_t capacity = getScsiCapacity();
\r
190 if (((uint64) lba) + blocks > capacity)
\r
192 scsiDev.status = CHECK_CONDITION;
\r
193 scsiDev.sense.code = ILLEGAL_REQUEST;
\r
194 scsiDev.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
\r
195 scsiDev.phase = STATUS;
\r
199 transfer.dir = TRANSFER_READ;
\r
200 transfer.lba = lba;
\r
201 transfer.blocks = blocks;
\r
202 transfer.currentBlock = 0;
\r
203 scsiDev.phase = DATA_IN;
\r
204 scsiDev.dataLen = 0; // No data yet
\r
206 if ((blocks == 1) ||
\r
207 (((uint64) lba) + blocks == capacity)
\r
210 // We get errors on reading the last sector using a multi-sector
\r
212 transfer.multiBlock = 0;
\r
216 transfer.multiBlock = 1;
\r
222 static void doSeek(uint32 lba)
\r
224 if (lba >= getScsiCapacity())
\r
226 scsiDev.status = CHECK_CONDITION;
\r
227 scsiDev.sense.code = ILLEGAL_REQUEST;
\r
228 scsiDev.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
\r
229 scsiDev.phase = STATUS;
\r
233 static int doTestUnitReady()
\r
236 if (!(blockDev.state & DISK_STARTED))
\r
239 scsiDev.status = CHECK_CONDITION;
\r
240 scsiDev.sense.code = NOT_READY;
\r
241 scsiDev.sense.asc = LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED;
\r
242 scsiDev.phase = STATUS;
\r
244 else if (!(blockDev.state & DISK_PRESENT))
\r
247 scsiDev.status = CHECK_CONDITION;
\r
248 scsiDev.sense.code = NOT_READY;
\r
249 scsiDev.sense.asc = MEDIUM_NOT_PRESENT;
\r
250 scsiDev.phase = STATUS;
\r
252 else if (!(blockDev.state & DISK_INITIALISED))
\r
255 scsiDev.status = CHECK_CONDITION;
\r
256 scsiDev.sense.code = NOT_READY;
\r
257 scsiDev.sense.asc = LOGICAL_UNIT_NOT_READY_CAUSE_NOT_REPORTABLE;
\r
258 scsiDev.phase = STATUS;
\r
263 // Handle direct-access scsi device commands
\r
264 int scsiDiskCommand()
\r
266 int commandHandled = 1;
\r
268 uint8 command = scsiDev.cdb[0];
\r
269 if (command == 0x1B)
\r
272 // Enable or disable media access operations.
\r
273 // Ignore load/eject requests. We can't do that.
\r
274 //int immed = scsiDev.cdb[1] & 1;
\r
275 int start = scsiDev.cdb[4] & 1;
\r
279 blockDev.state = blockDev.state | DISK_STARTED;
\r
280 if (!(blockDev.state & DISK_INITIALISED))
\r
287 blockDev.state &= ~DISK_STARTED;
\r
290 else if (command == 0x00)
\r
295 else if (!doTestUnitReady())
\r
297 // Status and sense codes already set by doTestUnitReady
\r
299 else if (command == 0x04)
\r
302 // We don't really do any formatting, but we need to read the correct
\r
303 // number of bytes in the DATA_OUT phase to make the SCSI host happy.
\r
305 int fmtData = (scsiDev.cdb[1] & 0x10) ? 1 : 0;
\r
308 // We need to read the parameter list, but we don't know how
\r
309 // big it is yet. Start with the header.
\r
310 scsiDev.dataLen = 4;
\r
311 scsiDev.phase = DATA_OUT;
\r
312 scsiDev.postDataOutHook = doFormatUnitHeader;
\r
316 // No data to read, we're already finished!
\r
319 else if (command == 0x08)
\r
323 (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
\r
324 (((uint32) scsiDev.cdb[2]) << 8) +
\r
326 uint32 blocks = scsiDev.cdb[4];
\r
327 if (blocks == 0) blocks = 256;
\r
328 doRead(lba, blocks);
\r
331 else if (command == 0x28)
\r
334 // Ignore all cache control bits - we don't support a memory cache.
\r
337 (((uint32) scsiDev.cdb[2]) << 24) +
\r
338 (((uint32) scsiDev.cdb[3]) << 16) +
\r
339 (((uint32) scsiDev.cdb[4]) << 8) +
\r
342 (((uint32) scsiDev.cdb[7]) << 8) +
\r
345 doRead(lba, blocks);
\r
348 else if (command == 0x25)
\r
354 else if (command == 0x0B)
\r
358 (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
\r
359 (((uint32) scsiDev.cdb[2]) << 8) +
\r
365 else if (command == 0x2B)
\r
369 (((uint32) scsiDev.cdb[2]) << 24) +
\r
370 (((uint32) scsiDev.cdb[3]) << 16) +
\r
371 (((uint32) scsiDev.cdb[4]) << 8) +
\r
376 else if (command == 0x0A)
\r
380 (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
\r
381 (((uint32) scsiDev.cdb[2]) << 8) +
\r
383 uint32 blocks = scsiDev.cdb[4];
\r
384 if (blocks == 0) blocks = 256;
\r
385 doWrite(lba, blocks);
\r
388 else if (command == 0x2A)
\r
391 // Ignore all cache control bits - we don't support a memory cache.
\r
394 (((uint32) scsiDev.cdb[2]) << 24) +
\r
395 (((uint32) scsiDev.cdb[3]) << 16) +
\r
396 (((uint32) scsiDev.cdb[4]) << 8) +
\r
399 (((uint32) scsiDev.cdb[7]) << 8) +
\r
402 doWrite(lba, blocks);
\r
404 else if (command == 0x36)
\r
406 // LOCK UNLOCK CACHE
\r
407 // We don't have a cache to lock data into. do nothing.
\r
409 else if (command == 0x34)
\r
412 // We don't have a cache to pre-fetch into. do nothing.
\r
414 else if (command == 0x1E)
\r
416 // PREVENT ALLOW MEDIUM REMOVAL
\r
417 // Not much we can do to prevent the user removing the SD card.
\r
420 else if (command == 0x01)
\r
423 // Set the lun to a vendor-specific state. Ignore.
\r
425 else if (command == 0x35)
\r
427 // SYNCHRONIZE CACHE
\r
428 // We don't have a cache. do nothing.
\r
430 else if (command == 0x2F)
\r
433 // TODO: When they supply data to verify, we should read the data and
\r
434 // verify it. If they don't supply any data, just say success.
\r
435 if ((scsiDev.cdb[1] & 0x02) == 0)
\r
437 // They are asking us to do a medium verification with no data
\r
438 // comparison. Assume success, do nothing.
\r
442 // TODO. This means they are supplying data to verify against.
\r
443 // Technically we should probably grab the data and compare it.
\r
444 scsiDev.status = CHECK_CONDITION;
\r
445 scsiDev.sense.code = ILLEGAL_REQUEST;
\r
446 scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
\r
447 scsiDev.phase = STATUS;
\r
452 commandHandled = 0;
\r
455 return commandHandled;
\r
458 void scsiDiskPoll()
\r
460 if (scsiDev.phase == DATA_IN &&
\r
461 transfer.currentBlock != transfer.blocks)
\r
463 if (scsiDev.dataLen == 0)
\r
465 if (transfer.multiBlock)
\r
467 sdReadSectorMulti();
\r
471 sdReadSectorSingle();
\r
474 else if (scsiDev.dataPtr == scsiDev.dataLen)
\r
476 scsiDev.dataLen = 0;
\r
477 scsiDev.dataPtr = 0;
\r
478 transfer.currentBlock++;
\r
479 if (transfer.currentBlock >= transfer.blocks)
\r
481 scsiDev.phase = STATUS;
\r
486 else if (scsiDev.phase == DATA_OUT &&
\r
487 transfer.currentBlock != transfer.blocks)
\r
490 // TODO FIX scsiDiskPoll() scsiDev.dataPtr = 0;
\r
491 transfer.currentBlock++;
\r
492 if (transfer.currentBlock >= transfer.blocks)
\r
494 scsiDev.dataLen = 0;
\r
495 scsiDev.dataPtr = 0;
\r
496 scsiDev.phase = STATUS;
\r
503 void scsiDiskReset()
\r
505 scsiDev.dataPtr = 0;
\r
506 scsiDev.savedDataPtr = 0;
\r
507 scsiDev.dataLen = 0;
\r
508 // transfer.lba = 0; // Needed in Request Sense to determine failure
\r
509 transfer.blocks = 0;
\r
510 transfer.currentBlock = 0;
\r
512 // Cancel long running commands!
\r
513 if (transfer.inProgress == 1)
\r
515 if (transfer.dir == TRANSFER_WRITE)
\r
524 transfer.inProgress = 0;
\r
525 transfer.multiBlock = 0;
\r
528 void scsiDiskInit()
\r
530 transfer.inProgress = 0;
\r
533 // Don't require the host to send us a START STOP UNIT command
\r
534 blockDev.state = DISK_STARTED;
\r
535 // WP pin not available for micro-sd
\r
536 // TODO read card WP register
\r
540 blockDev.state = blockDev.state | DISK_WP;
\r
544 if (SD_CD_Read() == 1)
\r
547 blockDev.state = blockDev.state | DISK_PRESENT;
\r
549 // Wait up to 5 seconds for the SD card to wake up.
\r
550 for (retry = 0; retry < 5; ++retry)
\r