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
32 BlockDevice blockDev;
\r
35 static int doSdInit()
\r
38 if (blockDev.state & DISK_PRESENT)
\r
44 blockDev.state = blockDev.state | DISK_INITIALISED;
\r
50 // Callback once all data has been read in the data out phase.
\r
51 static void doFormatUnitComplete(void)
\r
53 // TODO start writing the initialisation pattern to the SD
\r
55 scsiDev.phase = STATUS;
\r
58 static void doFormatUnitSkipData(int bytes)
\r
60 // We may not have enough memory to store the initialisation pattern and
\r
61 // defect list data. Since we're not making use of it yet anyway, just
\r
62 // discard the bytes.
\r
63 scsiEnterPhase(DATA_OUT);
\r
65 for (i = 0; i < bytes; ++i)
\r
71 // Callback from the data out phase.
\r
72 static void doFormatUnitPatternHeader(void)
\r
75 ((((uint16_t)scsiDev.data[2])) << 8) +
\r
79 ((((uint16_t)scsiDev.data[4 + 2])) << 8) +
\r
80 scsiDev.data[4 + 3];
\r
82 doFormatUnitSkipData(defectLength + patternLength);
\r
83 doFormatUnitComplete();
\r
86 // Callback from the data out phase.
\r
87 static void doFormatUnitHeader(void)
\r
89 int IP = (scsiDev.data[1] & 0x08) ? 1 : 0;
\r
90 int DSP = (scsiDev.data[1] & 0x04) ? 1 : 0;
\r
92 if (! DSP) // disable save parameters
\r
94 // Save the "MODE SELECT savable parameters"
\r
96 scsiDev.target->targetId,
\r
97 scsiDev.target->liveCfg.bytesPerSector);
\r
102 // We need to read the initialisation pattern header first.
\r
103 scsiDev.dataLen += 4;
\r
104 scsiDev.phase = DATA_OUT;
\r
105 scsiDev.postDataOutHook = doFormatUnitPatternHeader;
\r
109 // Read the defect list data
\r
111 ((((uint16_t)scsiDev.data[2])) << 8) +
\r
113 doFormatUnitSkipData(defectLength);
\r
114 doFormatUnitComplete();
\r
118 static void doReadCapacity()
\r
120 uint32_t lba = (((uint32) scsiDev.cdb[2]) << 24) +
\r
121 (((uint32) scsiDev.cdb[3]) << 16) +
\r
122 (((uint32) scsiDev.cdb[4]) << 8) +
\r
124 int pmi = scsiDev.cdb[8] & 1;
\r
126 uint32_t capacity = getScsiCapacity(
\r
127 scsiDev.target->cfg->sdSectorStart,
\r
128 scsiDev.target->liveCfg.bytesPerSector,
\r
129 scsiDev.target->cfg->scsiSectors);
\r
134 // We don't do anything with the "partial medium indicator", and
\r
135 // assume that delays are constant across each block. But the spec
\r
136 // says we must return this error if pmi is specified incorrectly.
\r
137 scsiDev.status = CHECK_CONDITION;
\r
138 scsiDev.target->sense.code = ILLEGAL_REQUEST;
\r
139 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
\r
140 scsiDev.phase = STATUS;
\r
142 else if (capacity > 0)
\r
144 uint32_t highestBlock = capacity - 1;
\r
146 scsiDev.data[0] = highestBlock >> 24;
\r
147 scsiDev.data[1] = highestBlock >> 16;
\r
148 scsiDev.data[2] = highestBlock >> 8;
\r
149 scsiDev.data[3] = highestBlock;
\r
151 uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
\r
152 scsiDev.data[4] = bytesPerSector >> 24;
\r
153 scsiDev.data[5] = bytesPerSector >> 16;
\r
154 scsiDev.data[6] = bytesPerSector >> 8;
\r
155 scsiDev.data[7] = bytesPerSector;
\r
156 scsiDev.dataLen = 8;
\r
157 scsiDev.phase = DATA_IN;
\r
161 scsiDev.status = CHECK_CONDITION;
\r
162 scsiDev.target->sense.code = NOT_READY;
\r
163 scsiDev.target->sense.asc = MEDIUM_NOT_PRESENT;
\r
164 scsiDev.phase = STATUS;
\r
168 static void doWrite(uint32 lba, uint32 blocks)
\r
170 if ((blockDev.state & DISK_WP) ||
\r
171 (scsiDev.target->cfg->deviceType == CONFIG_OPTICAL))
\r
174 scsiDev.status = CHECK_CONDITION;
\r
175 scsiDev.target->sense.code = ILLEGAL_REQUEST;
\r
176 scsiDev.target->sense.asc = WRITE_PROTECTED;
\r
177 scsiDev.phase = STATUS;
\r
179 else if (((uint64) lba) + blocks >
\r
181 scsiDev.target->cfg->sdSectorStart,
\r
182 scsiDev.target->liveCfg.bytesPerSector,
\r
183 scsiDev.target->cfg->scsiSectors
\r
186 scsiDev.status = CHECK_CONDITION;
\r
187 scsiDev.target->sense.code = ILLEGAL_REQUEST;
\r
188 scsiDev.target->sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
\r
189 scsiDev.phase = STATUS;
\r
193 transfer.dir = TRANSFER_WRITE;
\r
194 transfer.lba = lba;
\r
195 transfer.blocks = blocks;
\r
196 transfer.currentBlock = 0;
\r
197 scsiDev.phase = DATA_OUT;
\r
198 scsiDev.dataLen = scsiDev.target->liveCfg.bytesPerSector;
\r
199 scsiDev.dataPtr = scsiDev.target->liveCfg.bytesPerSector;
\r
201 // No need for single-block writes atm. Overhead of the
\r
202 // multi-block write is minimal.
\r
203 transfer.multiBlock = 1;
\r
205 sdWriteMultiSectorPrep();
\r
210 static void doRead(uint32 lba, uint32 blocks)
\r
212 uint32_t capacity = getScsiCapacity(
\r
213 scsiDev.target->cfg->sdSectorStart,
\r
214 scsiDev.target->liveCfg.bytesPerSector,
\r
215 scsiDev.target->cfg->scsiSectors);
\r
216 if (((uint64) lba) + blocks > capacity)
\r
218 scsiDev.status = CHECK_CONDITION;
\r
219 scsiDev.target->sense.code = ILLEGAL_REQUEST;
\r
220 scsiDev.target->sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
\r
221 scsiDev.phase = STATUS;
\r
225 transfer.dir = TRANSFER_READ;
\r
226 transfer.lba = lba;
\r
227 transfer.blocks = blocks;
\r
228 transfer.currentBlock = 0;
\r
229 scsiDev.phase = DATA_IN;
\r
230 scsiDev.dataLen = 0; // No data yet
\r
232 if ((blocks == 1) ||
\r
233 (((uint64) lba) + blocks == capacity)
\r
236 // We get errors on reading the last sector using a multi-sector
\r
238 transfer.multiBlock = 0;
\r
242 transfer.multiBlock = 1;
\r
243 sdReadMultiSectorPrep();
\r
248 static void doSeek(uint32 lba)
\r
252 scsiDev.target->cfg->sdSectorStart,
\r
253 scsiDev.target->liveCfg.bytesPerSector,
\r
254 scsiDev.target->cfg->scsiSectors)
\r
257 scsiDev.status = CHECK_CONDITION;
\r
258 scsiDev.target->sense.code = ILLEGAL_REQUEST;
\r
259 scsiDev.target->sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
\r
260 scsiDev.phase = STATUS;
\r
264 static int doTestUnitReady()
\r
267 if (!(blockDev.state & DISK_STARTED))
\r
270 scsiDev.status = CHECK_CONDITION;
\r
271 scsiDev.target->sense.code = NOT_READY;
\r
272 scsiDev.target->sense.asc = LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED;
\r
273 scsiDev.phase = STATUS;
\r
275 else if (!(blockDev.state & DISK_PRESENT))
\r
278 scsiDev.status = CHECK_CONDITION;
\r
279 scsiDev.target->sense.code = NOT_READY;
\r
280 scsiDev.target->sense.asc = MEDIUM_NOT_PRESENT;
\r
281 scsiDev.phase = STATUS;
\r
283 else if (!(blockDev.state & DISK_INITIALISED))
\r
286 scsiDev.status = CHECK_CONDITION;
\r
287 scsiDev.target->sense.code = NOT_READY;
\r
288 scsiDev.target->sense.asc = LOGICAL_UNIT_NOT_READY_CAUSE_NOT_REPORTABLE;
\r
289 scsiDev.phase = STATUS;
\r
294 // Handle direct-access scsi device commands
\r
295 int scsiDiskCommand()
\r
297 int commandHandled = 1;
\r
299 uint8 command = scsiDev.cdb[0];
\r
300 if (command == 0x1B)
\r
303 // Enable or disable media access operations.
\r
304 // Ignore load/eject requests. We can't do that.
\r
305 //int immed = scsiDev.cdb[1] & 1;
\r
306 int start = scsiDev.cdb[4] & 1;
\r
310 blockDev.state = blockDev.state | DISK_STARTED;
\r
311 if (!(blockDev.state & DISK_INITIALISED))
\r
318 blockDev.state &= ~DISK_STARTED;
\r
321 else if (command == 0x00)
\r
326 else if (!doTestUnitReady())
\r
328 // Status and sense codes already set by doTestUnitReady
\r
330 else if (command == 0x04)
\r
333 // We don't really do any formatting, but we need to read the correct
\r
334 // number of bytes in the DATA_OUT phase to make the SCSI host happy.
\r
336 int fmtData = (scsiDev.cdb[1] & 0x10) ? 1 : 0;
\r
339 // We need to read the parameter list, but we don't know how
\r
340 // big it is yet. Start with the header.
\r
341 scsiDev.dataLen = 4;
\r
342 scsiDev.phase = DATA_OUT;
\r
343 scsiDev.postDataOutHook = doFormatUnitHeader;
\r
347 // No data to read, we're already finished!
\r
350 else if (command == 0x08)
\r
354 (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
\r
355 (((uint32) scsiDev.cdb[2]) << 8) +
\r
357 uint32 blocks = scsiDev.cdb[4];
\r
358 if (blocks == 0) blocks = 256;
\r
359 doRead(lba, blocks);
\r
362 else if (command == 0x28)
\r
365 // Ignore all cache control bits - we don't support a memory cache.
\r
368 (((uint32) scsiDev.cdb[2]) << 24) +
\r
369 (((uint32) scsiDev.cdb[3]) << 16) +
\r
370 (((uint32) scsiDev.cdb[4]) << 8) +
\r
373 (((uint32) scsiDev.cdb[7]) << 8) +
\r
376 doRead(lba, blocks);
\r
379 else if (command == 0x25)
\r
385 else if (command == 0x0B)
\r
389 (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
\r
390 (((uint32) scsiDev.cdb[2]) << 8) +
\r
396 else if (command == 0x2B)
\r
400 (((uint32) scsiDev.cdb[2]) << 24) +
\r
401 (((uint32) scsiDev.cdb[3]) << 16) +
\r
402 (((uint32) scsiDev.cdb[4]) << 8) +
\r
407 else if (command == 0x0A)
\r
411 (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
\r
412 (((uint32) scsiDev.cdb[2]) << 8) +
\r
414 uint32 blocks = scsiDev.cdb[4];
\r
415 if (blocks == 0) blocks = 256;
\r
416 doWrite(lba, blocks);
\r
419 else if (command == 0x2A)
\r
422 // Ignore all cache control bits - we don't support a memory cache.
\r
425 (((uint32) scsiDev.cdb[2]) << 24) +
\r
426 (((uint32) scsiDev.cdb[3]) << 16) +
\r
427 (((uint32) scsiDev.cdb[4]) << 8) +
\r
430 (((uint32) scsiDev.cdb[7]) << 8) +
\r
433 doWrite(lba, blocks);
\r
435 else if (command == 0x36)
\r
437 // LOCK UNLOCK CACHE
\r
438 // We don't have a cache to lock data into. do nothing.
\r
440 else if (command == 0x34)
\r
443 // We don't have a cache to pre-fetch into. do nothing.
\r
445 else if (command == 0x1E)
\r
447 // PREVENT ALLOW MEDIUM REMOVAL
\r
448 // Not much we can do to prevent the user removing the SD card.
\r
451 else if (command == 0x01)
\r
454 // Set the lun to a vendor-specific state. Ignore.
\r
456 else if (command == 0x35)
\r
458 // SYNCHRONIZE CACHE
\r
459 // We don't have a cache. do nothing.
\r
461 else if (command == 0x2F)
\r
464 // TODO: When they supply data to verify, we should read the data and
\r
465 // verify it. If they don't supply any data, just say success.
\r
466 if ((scsiDev.cdb[1] & 0x02) == 0)
\r
468 // They are asking us to do a medium verification with no data
\r
469 // comparison. Assume success, do nothing.
\r
473 // TODO. This means they are supplying data to verify against.
\r
474 // Technically we should probably grab the data and compare it.
\r
475 scsiDev.status = CHECK_CONDITION;
\r
476 scsiDev.target->sense.code = ILLEGAL_REQUEST;
\r
477 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
\r
478 scsiDev.phase = STATUS;
\r
483 commandHandled = 0;
\r
486 return commandHandled;
\r
489 void scsiDiskPoll()
\r
491 debugPause(); // TODO comment re. timeouts.
\r
493 if (scsiDev.phase == DATA_IN &&
\r
494 transfer.currentBlock != transfer.blocks)
\r
496 scsiEnterPhase(DATA_IN);
\r
498 int totalSDSectors =
\r
500 SDSectorsPerSCSISector(scsiDev.target->liveCfg.bytesPerSector);
\r
503 scsiDev.target->cfg->sdSectorStart,
\r
504 scsiDev.target->liveCfg.bytesPerSector,
\r
507 const int sdPerScsi =
\r
508 SDSectorsPerSCSISector(scsiDev.target->liveCfg.bytesPerSector);
\r
509 int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE;
\r
512 int scsiActive = 0;
\r
514 while ((i < totalSDSectors) &&
\r
515 (scsiDev.phase == DATA_IN) &&
\r
516 !scsiDev.resetFlag)
\r
518 if ((sdActive == 1) && sdReadSectorDMAPoll())
\r
523 else if ((sdActive == 0) && (prep - i < buffers) && (prep < totalSDSectors))
\r
525 // Start an SD transfer if we have space.
\r
526 if (transfer.multiBlock)
\r
528 sdReadMultiSectorDMA(&scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);
\r
532 sdReadSingleSectorDMA(sdLBA + prep, &scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);
\r
537 if ((scsiActive == 1) && scsiWriteDMAPoll())
\r
542 else if ((scsiActive == 0) && ((prep - i) > 0))
\r
544 int dmaBytes = SD_SECTOR_SIZE;
\r
545 if ((i % sdPerScsi) == (sdPerScsi - 1))
\r
547 dmaBytes = scsiDev.target->liveCfg.bytesPerSector % SD_SECTOR_SIZE;
\r
548 if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE;
\r
550 scsiWriteDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)], dmaBytes);
\r
554 if (scsiDev.phase == DATA_IN)
\r
556 scsiDev.phase = STATUS;
\r
560 else if (scsiDev.phase == DATA_OUT &&
\r
561 transfer.currentBlock != transfer.blocks)
\r
563 scsiEnterPhase(DATA_OUT);
\r
565 const int sdPerScsi =
\r
566 SDSectorsPerSCSISector(scsiDev.target->liveCfg.bytesPerSector);
\r
567 int totalSDSectors = transfer.blocks * sdPerScsi;
\r
568 int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE;
\r
571 int scsiDisconnected = 0;
\r
572 int scsiComplete = 0;
\r
573 uint32_t lastActivityTime = getTime_ms();
\r
574 int scsiActive = 0;
\r
577 while ((i < totalSDSectors) &&
\r
578 ((scsiDev.phase == DATA_OUT) || // scsiDisconnect keeps our phase.
\r
580 !scsiDev.resetFlag)
\r
582 if ((sdActive == 1) && sdWriteSectorDMAPoll(i == (totalSDSectors - 1)))
\r
587 else if ((sdActive == 0) && ((prep - i) > 0))
\r
589 // Start an SD transfer if we have space.
\r
590 sdWriteMultiSectorDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)]);
\r
594 uint32_t now = getTime_ms();
\r
596 if ((scsiActive == 1) && scsiReadDMAPoll())
\r
600 lastActivityTime = now;
\r
602 else if ((scsiActive == 0) &&
\r
603 ((prep - i) < buffers) &&
\r
604 (prep < totalSDSectors) &&
\r
607 int dmaBytes = SD_SECTOR_SIZE;
\r
608 if ((prep % sdPerScsi) == (sdPerScsi - 1))
\r
610 dmaBytes = scsiDev.target->liveCfg.bytesPerSector % SD_SECTOR_SIZE;
\r
611 if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE;
\r
613 scsiReadDMA(&scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)], dmaBytes);
\r
617 (scsiActive == 0) &&
\r
618 !scsiDisconnected &&
\r
619 scsiDev.discPriv &&
\r
620 (diffTime_ms(lastActivityTime, now) >= 20) &&
\r
621 (scsiDev.phase == DATA_OUT))
\r
623 // We're transferring over the SCSI bus faster than the SD card
\r
624 // can write. There is no more buffer space once we've finished
\r
625 // this SCSI transfer.
\r
626 // The NCR 53C700 interface chips have a 250ms "byte-to-byte"
\r
627 // timeout buffer. SD card writes are supposed to complete
\r
628 // within 200ms, but sometimes they don't.
\r
629 // The NCR 53C700 series is used on HP 9000 workstations.
\r
631 scsiDisconnected = 1;
\r
632 lastActivityTime = getTime_ms();
\r
634 else if (scsiDisconnected &&
\r
636 (prep == i) || // Buffers empty.
\r
637 // Send some messages every 100ms so we don't timeout.
\r
638 // At a minimum, a reselection involves an IDENTIFY message.
\r
639 (diffTime_ms(lastActivityTime, now) >= 100)
\r
642 int reconnected = scsiReconnect();
\r
645 scsiDisconnected = 0;
\r
646 lastActivityTime = getTime_ms(); // Don't disconnect immediately.
\r
648 else if (diffTime_ms(lastActivityTime, getTime_ms()) >= 10000)
\r
650 // Give up after 10 seconds of trying to reconnect.
\r
651 scsiDev.resetFlag = 1;
\r
657 (prep == totalSDSectors) && // All scsi data read and buffered
\r
658 !scsiDev.discPriv && // Prefer disconnect where possible.
\r
659 (diffTime_ms(lastActivityTime, now) >= 150) &&
\r
661 (scsiDev.phase == DATA_OUT) &&
\r
662 !(scsiDev.cdb[scsiDev.cdbLen - 1] & 0x01) // Not linked command
\r
665 // We're transferring over the SCSI bus faster than the SD card
\r
666 // can write. All data is buffered, and we're just waiting for
\r
667 // the SD card to complete. The host won't let us disconnect.
\r
668 // Some drivers set a 250ms timeout on transfers to complete.
\r
669 // SD card writes are supposed to complete
\r
670 // within 200ms, but sometimes they don'to.
\r
671 // Just pretend we're finished.
\r
675 process_MessageIn(); // Will go to BUS_FREE state
\r
677 // Try and prevent anyone else using the SCSI bus while we're not ready.
\r
678 SCSI_SetPin(SCSI_Out_BSY);
\r
684 SCSI_ClearPin(SCSI_Out_BSY);
\r
687 !scsiDev.resetFlag &&
\r
688 scsiDisconnected &&
\r
689 (diffTime_ms(lastActivityTime, getTime_ms()) <= 10000))
\r
691 scsiDisconnected = !scsiReconnect();
\r
693 if (scsiDisconnected)
\r
695 // Failed to reconnect
\r
696 scsiDev.resetFlag = 1;
\r
699 if (scsiDev.phase == DATA_OUT)
\r
701 if (scsiDev.parityError &&
\r
702 (scsiDev.target->cfg->flags & CONFIG_ENABLE_PARITY) &&
\r
703 !scsiDev.compatMode)
\r
705 scsiDev.target->sense.code = ABORTED_COMMAND;
\r
706 scsiDev.target->sense.asc = SCSI_PARITY_ERROR;
\r
707 scsiDev.status = CHECK_CONDITION;;
\r
709 scsiDev.phase = STATUS;
\r
713 debugResume(); // TODO comment re. timeouts.
\r
716 void scsiDiskReset()
\r
718 scsiDev.dataPtr = 0;
\r
719 scsiDev.savedDataPtr = 0;
\r
720 scsiDev.dataLen = 0;
\r
721 // transfer.lba = 0; // Needed in Request Sense to determine failure
\r
722 transfer.blocks = 0;
\r
723 transfer.currentBlock = 0;
\r
725 // Cancel long running commands!
\r
726 if (transfer.inProgress == 1)
\r
728 if (transfer.dir == TRANSFER_WRITE)
\r
737 transfer.inProgress = 0;
\r
738 transfer.multiBlock = 0;
\r
743 void scsiDiskInit()
\r
745 transfer.inProgress = 0;
\r
748 // Don't require the host to send us a START STOP UNIT command
\r
749 blockDev.state = DISK_STARTED;
\r
750 // WP pin not available for micro-sd
\r
751 // TODO read card WP register
\r
755 blockDev.state = blockDev.state | DISK_WP;
\r