SCSI2SD/software/SCSI2SD/src/disk.c

885 lines
23 KiB
C
Executable File

// Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
// Copyright (C) 2014 Doug Brown <doug@downtowndougbrown.com>
//
// This file is part of SCSI2SD.
//
// SCSI2SD is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// SCSI2SD is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
#include "device.h"
#include "scsi.h"
#include "scsiPhy.h"
#include "config.h"
#include "debug.h"
#include "debug.h"
#include "disk.h"
#include "sd.h"
#include "time.h"
#include <string.h>
// Global
Transfer transfer;
// Callback once all data has been read in the data out phase.
static void doFormatUnitComplete(void)
{
// TODO start writing the initialisation pattern to the SD
// card
scsiDev.phase = STATUS;
}
static void doFormatUnitSkipData(int bytes)
{
// We may not have enough memory to store the initialisation pattern and
// defect list data. Since we're not making use of it yet anyway, just
// discard the bytes.
scsiEnterPhase(DATA_OUT);
int i;
for (i = 0; i < bytes; ++i)
{
scsiReadByte();
}
}
// Callback from the data out phase.
static void doFormatUnitPatternHeader(void)
{
int defectLength =
((((uint16_t)scsiDev.data[2])) << 8) +
scsiDev.data[3];
int patternLength =
((((uint16_t)scsiDev.data[4 + 2])) << 8) +
scsiDev.data[4 + 3];
doFormatUnitSkipData(defectLength + patternLength);
doFormatUnitComplete();
}
// Callback from the data out phase.
static void doFormatUnitHeader(void)
{
int IP = (scsiDev.data[1] & 0x08) ? 1 : 0;
int DSP = (scsiDev.data[1] & 0x04) ? 1 : 0;
if (! DSP) // disable save parameters
{
// Save the "MODE SELECT savable parameters"
configSave(
scsiDev.target->cfg->scsiId & CONFIG_TARGET_ID_BITS,
scsiDev.target->state.bytesPerSector);
}
if (IP)
{
// We need to read the initialisation pattern header first.
scsiDev.dataLen += 4;
scsiDev.phase = DATA_OUT;
scsiDev.postDataOutHook = doFormatUnitPatternHeader;
}
else
{
// Read the defect list data
int defectLength =
((((uint16_t)scsiDev.data[2])) << 8) +
scsiDev.data[3];
doFormatUnitSkipData(defectLength);
doFormatUnitComplete();
}
}
static void doReadCapacity()
{
uint32_t lba = (((uint32) scsiDev.cdb[2]) << 24) +
(((uint32) scsiDev.cdb[3]) << 16) +
(((uint32) scsiDev.cdb[4]) << 8) +
scsiDev.cdb[5];
int pmi = scsiDev.cdb[8] & 1;
uint32_t capacity = getScsiCapacity(
scsiDev.target->device,
scsiDev.target->cfg->sdSectorStart,
scsiDev.target->state.bytesPerSector,
scsiDev.target->cfg->scsiSectors);
if (!pmi && lba)
{
// error.
// We don't do anything with the "partial medium indicator", and
// assume that delays are constant across each block. But the spec
// says we must return this error if pmi is specified incorrectly.
scsiDev.status = CHECK_CONDITION;
scsiDev.target->state.sense.code = ILLEGAL_REQUEST;
scsiDev.target->state.sense.asc = INVALID_FIELD_IN_CDB;
scsiDev.phase = STATUS;
}
else if (capacity > 0)
{
uint32_t highestBlock = capacity - 1;
scsiDev.data[0] = highestBlock >> 24;
scsiDev.data[1] = highestBlock >> 16;
scsiDev.data[2] = highestBlock >> 8;
scsiDev.data[3] = highestBlock;
uint32_t bytesPerSector = scsiDev.target->state.bytesPerSector;
scsiDev.data[4] = bytesPerSector >> 24;
scsiDev.data[5] = bytesPerSector >> 16;
scsiDev.data[6] = bytesPerSector >> 8;
scsiDev.data[7] = bytesPerSector;
scsiDev.dataLen = 8;
scsiDev.phase = DATA_IN;
}
else
{
scsiDev.status = CHECK_CONDITION;
scsiDev.target->state.sense.code = NOT_READY;
scsiDev.target->state.sense.asc = MEDIUM_NOT_PRESENT;
scsiDev.phase = STATUS;
}
}
static void doWrite(uint32 lba, uint32 blocks)
{
if (unlikely(scsiDev.target->cfg->deviceType == CONFIG_FLOPPY_14MB)) {
// Floppies are supposed to be slow. Some systems can't handle a floppy
// without an access time
CyDelay(10);
}
uint32_t bytesPerSector = scsiDev.target->state.bytesPerSector;
MEDIA_STATE* mediaState = &(scsiDev.target->device->mediaState);
if (unlikely(*mediaState & MEDIA_WP) ||
unlikely(scsiDev.target->cfg->deviceType == CONFIG_OPTICAL) ||
(scsiDev.target->cfg->storageDevice != CONFIG_STOREDEVICE_SD))
{
scsiDev.status = CHECK_CONDITION;
scsiDev.target->state.sense.code = ILLEGAL_REQUEST;
scsiDev.target->state.sense.asc = WRITE_PROTECTED;
scsiDev.phase = STATUS;
}
else if (unlikely(((uint64) lba) + blocks >
getScsiCapacity(
scsiDev.target->device,
scsiDev.target->cfg->sdSectorStart,
bytesPerSector,
scsiDev.target->cfg->scsiSectors
)
))
{
scsiDev.status = CHECK_CONDITION;
scsiDev.target->state.sense.code = ILLEGAL_REQUEST;
scsiDev.target->state.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
scsiDev.phase = STATUS;
}
else
{
transfer.lba = lba;
transfer.blocks = blocks;
transfer.currentBlock = 0;
scsiDev.phase = DATA_OUT;
scsiDev.dataLen = bytesPerSector;
scsiDev.dataPtr = bytesPerSector;
// No need for single-block writes atm. Overhead of the
// multi-block write is minimal.
transfer.multiBlock = 1;
uint32_t sdLBA =
SCSISector2SD(
scsiDev.target->cfg->sdSectorStart,
bytesPerSector,
lba);
uint32_t sdBlocks = blocks * SDSectorsPerSCSISector(bytesPerSector);
sdWriteMultiSectorPrep(sdLBA, sdBlocks);
}
}
static void doRead(uint32 lba, uint32 blocks)
{
if (unlikely(scsiDev.target->cfg->deviceType == CONFIG_FLOPPY_14MB)) {
// Floppies are supposed to be slow. Some systems can't handle a floppy
// without an access time
CyDelay(10);
}
uint32_t capacity = getScsiCapacity(
scsiDev.target->device,
scsiDev.target->cfg->sdSectorStart,
scsiDev.target->state.bytesPerSector,
scsiDev.target->cfg->scsiSectors);
if (unlikely(((uint64) lba) + blocks > capacity))
{
scsiDev.status = CHECK_CONDITION;
scsiDev.target->state.sense.code = ILLEGAL_REQUEST;
scsiDev.target->state.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
scsiDev.phase = STATUS;
}
else
{
transfer.lba = lba;
transfer.blocks = blocks;
transfer.currentBlock = 0;
scsiDev.phase = DATA_IN;
scsiDev.dataLen = 0; // No data yet
uint32_t bytesPerSector = scsiDev.target->state.bytesPerSector;
uint32_t sdSectorPerSCSISector = SDSectorsPerSCSISector(bytesPerSector);
uint32_t sdSectors =
blocks * sdSectorPerSCSISector;
if ((
(sdSectors == 1) &&
!(scsiDev.boardCfg.flags & CONFIG_ENABLE_CACHE)
) ||
unlikely(((uint64) lba) + blocks == capacity) ||
(scsiDev.target->cfg->storageDevice != CONFIG_STOREDEVICE_SD)
)
{
// We get errors on reading the last sector using a multi-sector
// read :-(
transfer.multiBlock = 0;
}
else
{
transfer.multiBlock = 1;
uint32_t sdLBA =
SCSISector2SD(
scsiDev.target->cfg->sdSectorStart,
bytesPerSector,
lba);
sdReadMultiSectorPrep(sdLBA, sdSectors);
}
}
}
static void doSeek(uint32 lba)
{
if (lba >=
getScsiCapacity(
scsiDev.target->device,
scsiDev.target->cfg->sdSectorStart,
scsiDev.target->state.bytesPerSector,
scsiDev.target->cfg->scsiSectors)
)
{
scsiDev.status = CHECK_CONDITION;
scsiDev.target->state.sense.code = ILLEGAL_REQUEST;
scsiDev.target->state.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
scsiDev.phase = STATUS;
}
else
{
CyDelay(10);
}
}
static int doTestUnitReady()
{
MEDIA_STATE* mediaState = &(scsiDev.target->device->mediaState);
int ready = 1;
if (likely(*mediaState == (MEDIA_STARTED | MEDIA_PRESENT | MEDIA_INITIALISED)))
{
// nothing to do.
}
else if (unlikely(!(*mediaState & MEDIA_STARTED)))
{
ready = 0;
scsiDev.status = CHECK_CONDITION;
scsiDev.target->state.sense.code = NOT_READY;
scsiDev.target->state.sense.asc = LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED;
scsiDev.phase = STATUS;
}
else if (unlikely(!(*mediaState & MEDIA_PRESENT)))
{
ready = 0;
scsiDev.status = CHECK_CONDITION;
scsiDev.target->state.sense.code = NOT_READY;
scsiDev.target->state.sense.asc = MEDIUM_NOT_PRESENT;
scsiDev.phase = STATUS;
}
else if (unlikely(!(*mediaState & MEDIA_INITIALISED)))
{
ready = 0;
scsiDev.status = CHECK_CONDITION;
scsiDev.target->state.sense.code = NOT_READY;
scsiDev.target->state.sense.asc = LOGICAL_UNIT_NOT_READY_CAUSE_NOT_REPORTABLE;
scsiDev.phase = STATUS;
}
return ready;
}
// Handle direct-access scsi device commands
int scsiDiskCommand()
{
int commandHandled = 1;
uint8 command = scsiDev.cdb[0];
if (unlikely(command == 0x1B))
{
// START STOP UNIT
// Enable or disable media access operations.
// Ignore load/eject requests. We can't do that.
//int immed = scsiDev.cdb[1] & 1;
int start = scsiDev.cdb[4] & 1;
MEDIA_STATE* mediaState = &(scsiDev.target->device->mediaState);
if (start)
{
*mediaState = *mediaState | MEDIA_STARTED;
if (!(*mediaState & MEDIA_INITIALISED))
{
if (*mediaState & MEDIA_PRESENT)
{
*mediaState = *mediaState | MEDIA_INITIALISED;
}
}
}
else
{
*mediaState &= ~MEDIA_STARTED;
}
}
else if (unlikely(command == 0x00))
{
// TEST UNIT READY
doTestUnitReady();
}
else if (unlikely(!doTestUnitReady()))
{
// Status and sense codes already set by doTestUnitReady
}
else if (likely(command == 0x08))
{
// READ(6)
uint32 lba =
(((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
(((uint32) scsiDev.cdb[2]) << 8) +
scsiDev.cdb[3];
uint32 blocks = scsiDev.cdb[4];
if (unlikely(blocks == 0)) blocks = 256;
doRead(lba, blocks);
}
else if (likely(command == 0x28))
{
// READ(10)
// Ignore all cache control bits - we don't support a memory cache.
uint32 lba =
(((uint32) scsiDev.cdb[2]) << 24) +
(((uint32) scsiDev.cdb[3]) << 16) +
(((uint32) scsiDev.cdb[4]) << 8) +
scsiDev.cdb[5];
uint32 blocks =
(((uint32) scsiDev.cdb[7]) << 8) +
scsiDev.cdb[8];
doRead(lba, blocks);
}
else if (likely(command == 0x0A))
{
// WRITE(6)
uint32 lba =
(((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
(((uint32) scsiDev.cdb[2]) << 8) +
scsiDev.cdb[3];
uint32 blocks = scsiDev.cdb[4];
if (unlikely(blocks == 0)) blocks = 256;
doWrite(lba, blocks);
}
else if (likely(command == 0x2A) || // WRITE(10)
unlikely(command == 0x2E)) // WRITE AND VERIFY
{
// Ignore all cache control bits - we don't support a memory cache.
// Don't bother verifying either. The SD card likely stores ECC
// along with each flash row.
uint32 lba =
(((uint32) scsiDev.cdb[2]) << 24) +
(((uint32) scsiDev.cdb[3]) << 16) +
(((uint32) scsiDev.cdb[4]) << 8) +
scsiDev.cdb[5];
uint32 blocks =
(((uint32) scsiDev.cdb[7]) << 8) +
scsiDev.cdb[8];
doWrite(lba, blocks);
}
else if (unlikely(command == 0x04))
{
// FORMAT UNIT
// We don't really do any formatting, but we need to read the correct
// number of bytes in the DATA_OUT phase to make the SCSI host happy.
int fmtData = (scsiDev.cdb[1] & 0x10) ? 1 : 0;
if (fmtData)
{
// We need to read the parameter list, but we don't know how
// big it is yet. Start with the header.
scsiDev.dataLen = 4;
scsiDev.phase = DATA_OUT;
scsiDev.postDataOutHook = doFormatUnitHeader;
}
else
{
// No data to read, we're already finished!
}
}
else if (unlikely(command == 0x25))
{
// READ CAPACITY
doReadCapacity();
}
else if (unlikely(command == 0x0B))
{
// SEEK(6)
uint32 lba =
(((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
(((uint32) scsiDev.cdb[2]) << 8) +
scsiDev.cdb[3];
doSeek(lba);
}
else if (unlikely(command == 0x2B))
{
// SEEK(10)
uint32 lba =
(((uint32) scsiDev.cdb[2]) << 24) +
(((uint32) scsiDev.cdb[3]) << 16) +
(((uint32) scsiDev.cdb[4]) << 8) +
scsiDev.cdb[5];
doSeek(lba);
}
else if (unlikely(command == 0x36))
{
// LOCK UNLOCK CACHE
// We don't have a cache to lock data into. do nothing.
}
else if (unlikely(command == 0x34))
{
// PRE-FETCH.
// We don't have a cache to pre-fetch into. do nothing.
}
else if (unlikely(command == 0x1E))
{
// PREVENT ALLOW MEDIUM REMOVAL
// Not much we can do to prevent the user removing the SD card.
// do nothing.
}
else if (unlikely(command == 0x01))
{
// REZERO UNIT
// Set the lun to a vendor-specific state. Ignore.
}
else if (unlikely(command == 0x35))
{
// SYNCHRONIZE CACHE
// We don't have a cache. do nothing.
}
else if (unlikely(command == 0x2F))
{
// VERIFY
// TODO: When they supply data to verify, we should read the data and
// verify it. If they don't supply any data, just say success.
if ((scsiDev.cdb[1] & 0x02) == 0)
{
// They are asking us to do a medium verification with no data
// comparison. Assume success, do nothing.
}
else
{
// TODO. This means they are supplying data to verify against.
// Technically we should probably grab the data and compare it.
scsiDev.status = CHECK_CONDITION;
scsiDev.target->state.sense.code = ILLEGAL_REQUEST;
scsiDev.target->state.sense.asc = INVALID_FIELD_IN_CDB;
scsiDev.phase = STATUS;
}
}
else if (unlikely(command == 0x37))
{
// READ DEFECT DATA
uint32_t allocLength = (((uint16_t)scsiDev.cdb[7]) << 8) |
scsiDev.cdb[8];
scsiDev.data[0] = 0;
scsiDev.data[1] = scsiDev.cdb[1];
scsiDev.data[2] = 0;
scsiDev.data[3] = 0;
scsiDev.dataLen = 4;
if (scsiDev.dataLen > allocLength)
{
scsiDev.dataLen = allocLength;
}
scsiDev.phase = DATA_IN;
}
else
{
commandHandled = 0;
}
return commandHandled;
}
void scsiDiskPoll()
{
uint32_t bytesPerSector = scsiDev.target->state.bytesPerSector;
if (scsiDev.phase == DATA_IN &&
transfer.currentBlock != transfer.blocks)
{
scsiEnterPhase(DATA_IN);
int totalSDSectors =
transfer.blocks * SDSectorsPerSCSISector(bytesPerSector);
uint32_t sdLBA =
SCSISector2SD(
scsiDev.target->cfg->sdSectorStart,
bytesPerSector,
transfer.lba);
const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector);
int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE;
int prep = 0;
int i = 0;
int scsiActive = 0;
int sdActive = 0;
int isSDDevice = scsiDev.target->cfg->storageDevice == CONFIG_STOREDEVICE_SD;
while ((i < totalSDSectors) &&
likely(scsiDev.phase == DATA_IN) &&
likely(!scsiDev.resetFlag))
{
// Wait for the next DMA interrupt. It's beneficial to halt the
// processor to give the DMA controller more memory bandwidth to
// work with.
int scsiBusy;
int sdBusy;
{
uint8_t intr = CyEnterCriticalSection();
scsiBusy = scsiDMABusy();
sdBusy = isSDDevice && sdDMABusy();
CyExitCriticalSection(intr);
}
while (scsiBusy && sdBusy && isSDDevice)
{
uint8_t intr = CyEnterCriticalSection();
scsiBusy = scsiDMABusy();
sdBusy = sdDMABusy();
if (scsiBusy && sdBusy)
{
__WFI();
}
CyExitCriticalSection(intr);
}
if (isSDDevice)
{
if (sdActive && !sdBusy && sdReadSectorDMAPoll())
{
sdActive = 0;
prep++;
}
}
else
{
S2S_Device* device = scsiDev.target->device;
if (sdActive && device->readAsyncPoll(device))
{
sdActive = 0;
prep++;
}
}
// Usually SD is slower than the SCSI interface.
// Prioritise starting the read of the next sector over starting a
// SCSI transfer for the last sector
// ie. NO "else" HERE.
if (!sdActive &&
(prep - i < buffers) &&
(prep < totalSDSectors))
{
if (isSDDevice)
{
// Start an SD transfer if we have space.
if (transfer.multiBlock)
{
sdReadMultiSectorDMA(&scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);
}
else
{
sdReadSingleSectorDMA(sdLBA + prep, &scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);
}
sdActive = 1;
}
else
{
// Sync Read onboard flash
S2S_Device* device = scsiDev.target->device;
device->readAsync(device, sdLBA + prep, 1, &scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)]);
sdActive = 1;
}
}
if (scsiActive && !scsiBusy && scsiWriteDMAPoll())
{
scsiActive = 0;
++i;
}
if (!scsiActive && ((prep - i) > 0))
{
int dmaBytes = SD_SECTOR_SIZE;
if ((i % sdPerScsi) == (sdPerScsi - 1))
{
dmaBytes = bytesPerSector % SD_SECTOR_SIZE;
if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE;
}
scsiWriteDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)], dmaBytes);
scsiActive = 1;
}
}
if (scsiDev.phase == DATA_IN)
{
scsiDev.phase = STATUS;
}
scsiDiskReset();
// Wait for current DMA transfer done then deselect (if reset encountered)
if (!isSDDevice)
{
S2S_Device* device = scsiDev.target->device;
while (!device->readAsyncPoll(device))
{
}
}
}
else if (scsiDev.phase == DATA_OUT &&
transfer.currentBlock != transfer.blocks)
{
scsiEnterPhase(DATA_OUT);
const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector);
int totalSDSectors = transfer.blocks * sdPerScsi;
int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE;
int prep = 0;
int i = 0;
int scsiDisconnected = 0;
int scsiComplete = 0;
int clearBSY = 0;
uint32_t lastActivityTime = getTime_ms();
int scsiActive = 0;
int sdActive = 0;
while ((i < totalSDSectors) &&
(likely(scsiDev.phase == DATA_OUT) || // scsiDisconnect keeps our phase.
scsiComplete) &&
likely(!scsiDev.resetFlag))
{
// Wait for the next DMA interrupt. It's beneficial to halt the
// processor to give the DMA controller more memory bandwidth to
// work with.
int scsiBusy = 1;
int sdBusy = 1;
while (scsiBusy && sdBusy)
{
uint8_t intr = CyEnterCriticalSection();
scsiBusy = scsiDMABusy();
sdBusy = sdDMABusy();
if (scsiBusy && sdBusy)
{
__WFI();
}
CyExitCriticalSection(intr);
}
if (sdActive && !sdBusy && sdWriteSectorDMAPoll())
{
sdActive = 0;
i++;
}
if (!sdActive && ((prep - i) > 0))
{
// Start an SD transfer if we have space.
sdWriteMultiSectorDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)]);
sdActive = 1;
}
uint32_t now = getTime_ms();
if (scsiActive && !scsiBusy && scsiReadDMAPoll())
{
scsiActive = 0;
++prep;
lastActivityTime = now;
}
if (!scsiActive &&
((prep - i) < buffers) &&
(prep < totalSDSectors) &&
likely(!scsiDisconnected))
{
int dmaBytes = SD_SECTOR_SIZE;
if ((prep % sdPerScsi) == (sdPerScsi - 1))
{
dmaBytes = bytesPerSector % SD_SECTOR_SIZE;
if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE;
}
scsiReadDMA(&scsiDev.data[SD_SECTOR_SIZE * (prep % buffers)], dmaBytes);
scsiActive = 1;
}
else if (
(scsiDev.boardCfg.flags & CONFIG_ENABLE_DISCONNECT) &&
(scsiActive == 0) &&
likely(!scsiDisconnected) &&
unlikely(scsiDev.discPriv) &&
unlikely(diffTime_ms(lastActivityTime, now) >= 20) &&
likely(scsiDev.phase == DATA_OUT))
{
// We're transferring over the SCSI bus faster than the SD card
// can write. There is no more buffer space once we've finished
// this SCSI transfer.
// The NCR 53C700 interface chips have a 250ms "byte-to-byte"
// timeout buffer. SD card writes are supposed to complete
// within 200ms, but sometimes they don't.
// The NCR 53C700 series is used on HP 9000 workstations.
scsiDisconnect();
scsiDisconnected = 1;
lastActivityTime = getTime_ms();
}
else if (unlikely(scsiDisconnected) &&
(
(prep == i) || // Buffers empty.
// Send some messages every 100ms so we don't timeout.
// At a minimum, a reselection involves an IDENTIFY message.
unlikely(diffTime_ms(lastActivityTime, now) >= 100)
))
{
int reconnected = scsiReconnect();
if (reconnected)
{
scsiDisconnected = 0;
lastActivityTime = getTime_ms(); // Don't disconnect immediately.
}
else if (diffTime_ms(lastActivityTime, getTime_ms()) >= 10000)
{
// Give up after 10 seconds of trying to reconnect.
scsiDev.resetFlag = 1;
}
}
else if (
likely(!scsiComplete) &&
(sdActive == 1) &&
(prep == totalSDSectors) && // All scsi data read and buffered
likely(!scsiDev.discPriv) && // Prefer disconnect where possible.
unlikely(diffTime_ms(lastActivityTime, now) >= 150) &&
likely(scsiDev.phase == DATA_OUT) &&
!(scsiDev.cdb[scsiDev.cdbLen - 1] & 0x01) // Not linked command
)
{
// We're transferring over the SCSI bus faster than the SD card
// can write. All data is buffered, and we're just waiting for
// the SD card to complete. The host won't let us disconnect.
// Some drivers set a 250ms timeout on transfers to complete.
// SD card writes are supposed to complete
// within 200ms, but sometimes they don'to.
// Just pretend we're finished.
scsiComplete = 1;
process_Status();
clearBSY = process_MessageIn(0); // Will go to BUS_FREE state but keeps BSY asserted
}
}
if (clearBSY)
{
enter_BusFree();
}
while (
!scsiDev.resetFlag &&
unlikely(scsiDisconnected) &&
(elapsedTime_ms(lastActivityTime) <= 10000))
{
scsiDisconnected = !scsiReconnect();
}
if (scsiDisconnected)
{
// Failed to reconnect
scsiDev.resetFlag = 1;
}
if (scsiDev.phase == DATA_OUT)
{
if (scsiDev.parityError &&
(scsiDev.boardCfg.flags & CONFIG_ENABLE_PARITY) &&
(scsiDev.compatMode >= COMPAT_SCSI2))
{
scsiDev.target->state.sense.code = ABORTED_COMMAND;
scsiDev.target->state.sense.asc = SCSI_PARITY_ERROR;
scsiDev.status = CHECK_CONDITION;;
}
scsiDev.phase = STATUS;
}
scsiDiskReset();
}
}
void scsiDiskReset()
{
scsiDev.dataPtr = 0;
scsiDev.savedDataPtr = 0;
scsiDev.dataLen = 0;
// transfer.lba = 0; // Needed in Request Sense to determine failure
transfer.blocks = 0;
transfer.currentBlock = 0;
// Cancel long running commands!
if (
((scsiDev.boardCfg.flags & CONFIG_ENABLE_CACHE) == 0) ||
(transfer.multiBlock == 0)
)
{
sdCompleteTransfer();
}
transfer.multiBlock = 0;
}
void scsiDiskInit()
{
scsiDiskReset();
// WP pin not available for micro-sd
// TODO read card WP register
#if 0
if (SD_WP_Read())
{
blockDev.state = blockDev.state | DISK_WP;
}
#endif
}