From 040354fac9d1a9dd2d553039c573f4ca48a92fc5 Mon Sep 17 00:00:00 2001 From: Michael McMaster Date: Thu, 13 May 2021 21:53:09 +1000 Subject: [PATCH] Don't send SD Complete Transfer command for single block writes --- src/firmware/bsp_driver_sd.c | 12 +- src/firmware/config.c | 2 +- src/firmware/disk.c | 1603 +++++++++++++++++----------------- src/firmware/main.c | 205 +++-- 4 files changed, 939 insertions(+), 883 deletions(-) diff --git a/src/firmware/bsp_driver_sd.c b/src/firmware/bsp_driver_sd.c index dda58d9d..e972c98f 100755 --- a/src/firmware/bsp_driver_sd.c +++ b/src/firmware/bsp_driver_sd.c @@ -248,6 +248,12 @@ uint8_t BSP_SD_ReadBlocks_DMA(uint8_t *pData, uint64_t BlockAddr, uint32_t NumOf { SD_state = MSD_OK; } + + HAL_SD_CardStateTypeDef cardState = HAL_SD_GetCardState(&hsd); + while (cardState == HAL_SD_CARD_PROGRAMMING || cardState == HAL_SD_CARD_SENDING) + { + cardState = HAL_SD_GetCardState(&hsd); + } } return SD_state; @@ -291,7 +297,11 @@ uint8_t BSP_SD_WriteBlocks_DMA(uint8_t *pData, uint64_t BlockAddr, uint32_t NumO } while (HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY) {} // Wait for DMA to complete - SDMMC_CmdStopTransfer(hsd.Instance); + + if (NumOfBlocks > 1) + { + SDMMC_CmdStopTransfer(hsd.Instance); + } if(HAL_SD_GetState(&hsd) == HAL_SD_STATE_ERROR) { diff --git a/src/firmware/config.c b/src/firmware/config.c index a016f6fc..109b856f 100755 --- a/src/firmware/config.c +++ b/src/firmware/config.c @@ -36,7 +36,7 @@ #include -static const uint16_t FIRMWARE_VERSION = 0x0645; +static const uint16_t FIRMWARE_VERSION = 0x0646; // Optional static config extern uint8_t* __fixed_config; diff --git a/src/firmware/disk.c b/src/firmware/disk.c index 26962d93..c9c7ae45 100755 --- a/src/firmware/disk.c +++ b/src/firmware/disk.c @@ -1,20 +1,20 @@ -// Copyright (C) 2013 Michael McMaster -// Copyright (C) 2014 Doug Brown +// Copyright (C) 2013 Michael McMaster +// Copyright (C) 2014 Doug Brown // -// This file is part of SCSI2SD. +// 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 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. +// 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 . +// You should have received a copy of the GNU General Public License +// along with SCSI2SD. If not, see . #ifdef STM32F2xx #include "stm32f2xx.h" @@ -46,728 +46,744 @@ Transfer transfer; static int doSdInit() { - int result = 0; - if (blockDev.state & DISK_PRESENT) - { - blockDev.state = blockDev.state | DISK_INITIALISED; - } - return result; + int result = 0; + if (blockDev.state & DISK_PRESENT) + { + blockDev.state = blockDev.state | DISK_INITIALISED; + } + return result; } // 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; + // 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(); - } + // 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 defectLength = + ((((uint16_t)scsiDev.data[2])) << 8) + + scsiDev.data[3]; - int patternLength = - ((((uint16_t)scsiDev.data[4 + 2])) << 8) + - scsiDev.data[4 + 3]; + int patternLength = + ((((uint16_t)scsiDev.data[4 + 2])) << 8) + + scsiDev.data[4 + 3]; - doFormatUnitSkipData(defectLength + patternLength); - doFormatUnitComplete(); + 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" - s2s_configSave( - scsiDev.target->targetId, - scsiDev.target->liveCfg.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(); - } + 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" + s2s_configSave( + scsiDev.target->targetId, + scsiDev.target->liveCfg.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_t) scsiDev.cdb[2]) << 24) + - (((uint32_t) scsiDev.cdb[3]) << 16) + - (((uint32_t) scsiDev.cdb[4]) << 8) + - scsiDev.cdb[5]; - int pmi = scsiDev.cdb[8] & 1; - - uint32_t capacity = getScsiCapacity( - scsiDev.target->cfg->sdSectorStart, - scsiDev.target->liveCfg.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->sense.code = ILLEGAL_REQUEST; - scsiDev.target->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->liveCfg.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->sense.code = NOT_READY; - scsiDev.target->sense.asc = MEDIUM_NOT_PRESENT; - scsiDev.phase = STATUS; - } + uint32_t lba = (((uint32_t) scsiDev.cdb[2]) << 24) + + (((uint32_t) scsiDev.cdb[3]) << 16) + + (((uint32_t) scsiDev.cdb[4]) << 8) + + scsiDev.cdb[5]; + int pmi = scsiDev.cdb[8] & 1; + + uint32_t capacity = getScsiCapacity( + scsiDev.target->cfg->sdSectorStart, + scsiDev.target->liveCfg.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->sense.code = ILLEGAL_REQUEST; + scsiDev.target->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->liveCfg.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->sense.code = NOT_READY; + scsiDev.target->sense.asc = MEDIUM_NOT_PRESENT; + scsiDev.phase = STATUS; + } } static void doWrite(uint32_t lba, uint32_t blocks) { - if (unlikely(scsiDev.target->cfg->deviceType == S2S_CFG_FLOPPY_14MB)) { - // Floppies are supposed to be slow. Some systems can't handle a floppy - // without an access time - s2s_delay_ms(10); - } - - uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector; - - if (unlikely(blockDev.state & DISK_WP) || - unlikely(scsiDev.target->cfg->deviceType == S2S_CFG_OPTICAL)) - - { - scsiDev.status = CHECK_CONDITION; - scsiDev.target->sense.code = ILLEGAL_REQUEST; - scsiDev.target->sense.asc = WRITE_PROTECTED; - scsiDev.phase = STATUS; - } - else if (unlikely(((uint64_t) lba) + blocks > - getScsiCapacity( - scsiDev.target->cfg->sdSectorStart, - bytesPerSector, - scsiDev.target->cfg->scsiSectors - ) - )) - { - scsiDev.status = CHECK_CONDITION; - scsiDev.target->sense.code = ILLEGAL_REQUEST; - scsiDev.target->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; - - - // TODO uint32_t sdLBA = -// TODO SCSISector2SD( - // TODO scsiDev.target->cfg->sdSectorStart, - // TODO bytesPerSector, - // TODO lba); - // TODO uint32_t sdBlocks = blocks * SDSectorsPerSCSISector(bytesPerSector); - // TODO sdWriteMultiSectorPrep(sdLBA, sdBlocks); - } + if (unlikely(scsiDev.target->cfg->deviceType == S2S_CFG_FLOPPY_14MB)) { + // Floppies are supposed to be slow. Some systems can't handle a floppy + // without an access time + s2s_delay_ms(10); + } + + uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector; + + if (unlikely(blockDev.state & DISK_WP) || + unlikely(scsiDev.target->cfg->deviceType == S2S_CFG_OPTICAL)) + + { + scsiDev.status = CHECK_CONDITION; + scsiDev.target->sense.code = ILLEGAL_REQUEST; + scsiDev.target->sense.asc = WRITE_PROTECTED; + scsiDev.phase = STATUS; + } + else if (unlikely(((uint64_t) lba) + blocks > + getScsiCapacity( + scsiDev.target->cfg->sdSectorStart, + bytesPerSector, + scsiDev.target->cfg->scsiSectors + ) + )) + { + scsiDev.status = CHECK_CONDITION; + scsiDev.target->sense.code = ILLEGAL_REQUEST; + scsiDev.target->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; + + + // TODO uint32_t sdLBA = +// TODO SCSISector2SD( + // TODO scsiDev.target->cfg->sdSectorStart, + // TODO bytesPerSector, + // TODO lba); + // TODO uint32_t sdBlocks = blocks * SDSectorsPerSCSISector(bytesPerSector); + // TODO sdWriteMultiSectorPrep(sdLBA, sdBlocks); + } } static void doRead(uint32_t lba, uint32_t blocks) { - if (unlikely(scsiDev.target->cfg->deviceType == S2S_CFG_FLOPPY_14MB)) { - // Floppies are supposed to be slow. Some systems can't handle a floppy - // without an access time - s2s_delay_ms(10); - } - - uint32_t capacity = getScsiCapacity( - scsiDev.target->cfg->sdSectorStart, - scsiDev.target->liveCfg.bytesPerSector, - scsiDev.target->cfg->scsiSectors); - if (unlikely(((uint64_t) lba) + blocks > capacity)) - { - scsiDev.status = CHECK_CONDITION; - scsiDev.target->sense.code = ILLEGAL_REQUEST; - scsiDev.target->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->liveCfg.bytesPerSector; - uint32_t sdSectorPerSCSISector = SDSectorsPerSCSISector(bytesPerSector); - uint32_t sdSectors = - blocks * sdSectorPerSCSISector; - - if (( - (sdSectors == 1) && - !(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_CACHE) - ) || - unlikely(((uint64_t) lba) + blocks == capacity) - ) - { - // 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); - - // TODO sdReadMultiSectorPrep(sdLBA, sdSectors); - } - } + if (unlikely(scsiDev.target->cfg->deviceType == S2S_CFG_FLOPPY_14MB)) { + // Floppies are supposed to be slow. Some systems can't handle a floppy + // without an access time + s2s_delay_ms(10); + } + + uint32_t capacity = getScsiCapacity( + scsiDev.target->cfg->sdSectorStart, + scsiDev.target->liveCfg.bytesPerSector, + scsiDev.target->cfg->scsiSectors); + if (unlikely(((uint64_t) lba) + blocks > capacity)) + { + scsiDev.status = CHECK_CONDITION; + scsiDev.target->sense.code = ILLEGAL_REQUEST; + scsiDev.target->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->liveCfg.bytesPerSector; + uint32_t sdSectorPerSCSISector = SDSectorsPerSCSISector(bytesPerSector); + uint32_t sdSectors = + blocks * sdSectorPerSCSISector; + + if (( + (sdSectors == 1) && + !(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_CACHE) + ) || + unlikely(((uint64_t) lba) + blocks == capacity) + ) + { + // 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); + + // TODO sdReadMultiSectorPrep(sdLBA, sdSectors); + } + } } static void doSeek(uint32_t lba) { - if (lba >= - getScsiCapacity( - scsiDev.target->cfg->sdSectorStart, - scsiDev.target->liveCfg.bytesPerSector, - scsiDev.target->cfg->scsiSectors) - ) - { - scsiDev.status = CHECK_CONDITION; - scsiDev.target->sense.code = ILLEGAL_REQUEST; - scsiDev.target->sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - scsiDev.phase = STATUS; - } - else - { - s2s_delay_ms(10); - } + if (lba >= + getScsiCapacity( + scsiDev.target->cfg->sdSectorStart, + scsiDev.target->liveCfg.bytesPerSector, + scsiDev.target->cfg->scsiSectors) + ) + { + scsiDev.status = CHECK_CONDITION; + scsiDev.target->sense.code = ILLEGAL_REQUEST; + scsiDev.target->sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + scsiDev.phase = STATUS; + } + else + { + s2s_delay_ms(10); + } } static int doTestUnitReady() { - int ready = 1; - if (likely(blockDev.state == (DISK_STARTED | DISK_PRESENT | DISK_INITIALISED))) - { - // nothing to do. - } - else if (unlikely(!(blockDev.state & DISK_STARTED))) - { - ready = 0; - scsiDev.status = CHECK_CONDITION; - scsiDev.target->sense.code = NOT_READY; - scsiDev.target->sense.asc = LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED; - scsiDev.phase = STATUS; - } - else if (unlikely(!(blockDev.state & DISK_PRESENT))) - { - ready = 0; - scsiDev.status = CHECK_CONDITION; - scsiDev.target->sense.code = NOT_READY; - scsiDev.target->sense.asc = MEDIUM_NOT_PRESENT; - scsiDev.phase = STATUS; - } - else if (unlikely(!(blockDev.state & DISK_INITIALISED))) - { - ready = 0; - scsiDev.status = CHECK_CONDITION; - scsiDev.target->sense.code = NOT_READY; - scsiDev.target->sense.asc = LOGICAL_UNIT_NOT_READY_CAUSE_NOT_REPORTABLE; - scsiDev.phase = STATUS; - } - return ready; + int ready = 1; + if (likely(blockDev.state == (DISK_STARTED | DISK_PRESENT | DISK_INITIALISED))) + { + // nothing to do. + } + else if (unlikely(!(blockDev.state & DISK_STARTED))) + { + ready = 0; + scsiDev.status = CHECK_CONDITION; + scsiDev.target->sense.code = NOT_READY; + scsiDev.target->sense.asc = LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED; + scsiDev.phase = STATUS; + } + else if (unlikely(!(blockDev.state & DISK_PRESENT))) + { + ready = 0; + scsiDev.status = CHECK_CONDITION; + scsiDev.target->sense.code = NOT_READY; + scsiDev.target->sense.asc = MEDIUM_NOT_PRESENT; + scsiDev.phase = STATUS; + } + else if (unlikely(!(blockDev.state & DISK_INITIALISED))) + { + ready = 0; + scsiDev.status = CHECK_CONDITION; + scsiDev.target->sense.code = NOT_READY; + scsiDev.target->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_t 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; - - if (start) - { - blockDev.state = blockDev.state | DISK_STARTED; - if (!(blockDev.state & DISK_INITIALISED)) - { - doSdInit(); - } - } - else - { - blockDev.state &= ~DISK_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_t lba = - (((uint32_t) scsiDev.cdb[1] & 0x1F) << 16) + - (((uint32_t) scsiDev.cdb[2]) << 8) + - scsiDev.cdb[3]; - uint32_t 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_t lba = - (((uint32_t) scsiDev.cdb[2]) << 24) + - (((uint32_t) scsiDev.cdb[3]) << 16) + - (((uint32_t) scsiDev.cdb[4]) << 8) + - scsiDev.cdb[5]; - uint32_t blocks = - (((uint32_t) scsiDev.cdb[7]) << 8) + - scsiDev.cdb[8]; - - doRead(lba, blocks); - } - else if (likely(command == 0x0A)) - { - // WRITE(6) - uint32_t lba = - (((uint32_t) scsiDev.cdb[1] & 0x1F) << 16) + - (((uint32_t) scsiDev.cdb[2]) << 8) + - scsiDev.cdb[3]; - uint32_t 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_t lba = - (((uint32_t) scsiDev.cdb[2]) << 24) + - (((uint32_t) scsiDev.cdb[3]) << 16) + - (((uint32_t) scsiDev.cdb[4]) << 8) + - scsiDev.cdb[5]; - uint32_t blocks = - (((uint32_t) 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_t lba = - (((uint32_t) scsiDev.cdb[1] & 0x1F) << 16) + - (((uint32_t) scsiDev.cdb[2]) << 8) + - scsiDev.cdb[3]; - - doSeek(lba); - } - - else if (unlikely(command == 0x2B)) - { - // SEEK(10) - uint32_t lba = - (((uint32_t) scsiDev.cdb[2]) << 24) + - (((uint32_t) scsiDev.cdb[3]) << 16) + - (((uint32_t) 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->sense.code = ILLEGAL_REQUEST; - scsiDev.target->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; + int commandHandled = 1; + + uint8_t 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; + + if (start) + { + blockDev.state = blockDev.state | DISK_STARTED; + if (!(blockDev.state & DISK_INITIALISED)) + { + doSdInit(); + } + } + else + { + blockDev.state &= ~DISK_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_t lba = + (((uint32_t) scsiDev.cdb[1] & 0x1F) << 16) + + (((uint32_t) scsiDev.cdb[2]) << 8) + + scsiDev.cdb[3]; + uint32_t 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_t lba = + (((uint32_t) scsiDev.cdb[2]) << 24) + + (((uint32_t) scsiDev.cdb[3]) << 16) + + (((uint32_t) scsiDev.cdb[4]) << 8) + + scsiDev.cdb[5]; + uint32_t blocks = + (((uint32_t) scsiDev.cdb[7]) << 8) + + scsiDev.cdb[8]; + + doRead(lba, blocks); + } + else if (likely(command == 0x0A)) + { + // WRITE(6) + uint32_t lba = + (((uint32_t) scsiDev.cdb[1] & 0x1F) << 16) + + (((uint32_t) scsiDev.cdb[2]) << 8) + + scsiDev.cdb[3]; + uint32_t 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_t lba = + (((uint32_t) scsiDev.cdb[2]) << 24) + + (((uint32_t) scsiDev.cdb[3]) << 16) + + (((uint32_t) scsiDev.cdb[4]) << 8) + + scsiDev.cdb[5]; + uint32_t blocks = + (((uint32_t) 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_t lba = + (((uint32_t) scsiDev.cdb[1] & 0x1F) << 16) + + (((uint32_t) scsiDev.cdb[2]) << 8) + + scsiDev.cdb[3]; + + doSeek(lba); + } + + else if (unlikely(command == 0x2B)) + { + // SEEK(10) + uint32_t lba = + (((uint32_t) scsiDev.cdb[2]) << 24) + + (((uint32_t) scsiDev.cdb[3]) << 16) + + (((uint32_t) 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->sense.code = ILLEGAL_REQUEST; + scsiDev.target->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->liveCfg.bytesPerSector; - - if (scsiDev.phase == DATA_IN && - transfer.currentBlock != transfer.blocks) - { - // Take responsibility for waiting for the phase delays - uint32_t phaseChangeDelayUs = scsiEnterPhaseImmediate(DATA_IN); - - int totalSDSectors = - transfer.blocks * SDSectorsPerSCSISector(bytesPerSector); - uint32_t sdLBA = - SCSISector2SD( - scsiDev.target->cfg->sdSectorStart, - bytesPerSector, - transfer.lba); - - const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector); - const int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE; - int prep = 0; - int i = 0; - int scsiActive __attribute__((unused)) = 0; // unused if DMA disabled - int sdActive = 0; - - // It's highly unlikely that someone is going to use huge transfers - // per scsi command, but if they do it'll be slower than usual. - uint32_t totalScsiBytes = transfer.blocks * bytesPerSector; - int useSlowDataCount = totalScsiBytes >= SCSI_XFER_MAX; - if (!useSlowDataCount) - { - scsiSetDataCount(totalScsiBytes); - } - - while ((i < totalSDSectors) && - likely(scsiDev.phase == DATA_IN) && - likely(!scsiDev.resetFlag)) - { - int completedDmaSectors; - if (sdActive && (completedDmaSectors = sdReadDMAPoll(sdActive))) - { - prep += completedDmaSectors; - sdActive -= completedDmaSectors; - } else if (sdActive > 1) - { - if ((scsiDev.data[SD_SECTOR_SIZE * (prep % buffers) + 510] != 0xAA) || - (scsiDev.data[SD_SECTOR_SIZE * (prep % buffers) + 511] != 0x33)) - { - prep += 1; - sdActive -= 1; - } - } - - if (!sdActive && - (prep - i < buffers) && - (prep < totalSDSectors) && - ((totalSDSectors - prep) >= sdPerScsi) && - (likely(!useSlowDataCount) || scsiPhyComplete()) && - (HAL_SD_GetState(&hsd) != HAL_SD_STATE_BUSY)) // rx complete but IRQ not fired yet. - { - // Start an SD transfer if we have space. - uint32_t startBuffer = prep % buffers; - uint32_t sectors = totalSDSectors - prep; - uint32_t freeBuffers = buffers - (prep - i); - - uint32_t contiguousBuffers = buffers - startBuffer; - freeBuffers = freeBuffers < contiguousBuffers - ? freeBuffers : contiguousBuffers; - sectors = sectors < freeBuffers ? sectors : freeBuffers; - - if (sectors > 128) sectors = 128; // 65536 DMA limit !! - - // Round-down when we have odd sector sizes. - if (sdPerScsi != 1) - { - sectors = (sectors / sdPerScsi) * sdPerScsi; - } - - for (int dodgy = 0; dodgy < sectors; dodgy++) - { - scsiDev.data[SD_SECTOR_SIZE * (startBuffer + dodgy) + 510] = 0xAA; - scsiDev.data[SD_SECTOR_SIZE * (startBuffer + dodgy) + 511] = 0x33; - } - - sdReadDMA(sdLBA + prep, sectors, &scsiDev.data[SD_SECTOR_SIZE * startBuffer]); - - sdActive = sectors; - - if (useSlowDataCount) - { - scsiSetDataCount((sectors / sdPerScsi) * bytesPerSector); - } - - // Wait now that the SD card is busy - // Chances are we've probably already waited sufficient time, - // but it's hard to measure microseconds cheaply. So just wait - // extra just-in-case. Hopefully it's in parallel with dma. - if (phaseChangeDelayUs > 0) - { - s2s_delay_us(phaseChangeDelayUs); - phaseChangeDelayUs = 0; - } - } - - if (((prep - i) > 0) && + uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector; + + if (scsiDev.phase == DATA_IN && + transfer.currentBlock != transfer.blocks) + { + // Take responsibility for waiting for the phase delays + uint32_t phaseChangeDelayUs = scsiEnterPhaseImmediate(DATA_IN); + + int totalSDSectors = + transfer.blocks * SDSectorsPerSCSISector(bytesPerSector); + uint32_t sdLBA = + SCSISector2SD( + scsiDev.target->cfg->sdSectorStart, + bytesPerSector, + transfer.lba); + + const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector); + const int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE; + int prep = 0; + int i = 0; + int scsiActive __attribute__((unused)) = 0; // unused if DMA disabled + int sdActive = 0; + + // It's highly unlikely that someone is going to use huge transfers + // per scsi command, but if they do it'll be slower than usual. + uint32_t totalScsiBytes = transfer.blocks * bytesPerSector; + int useSlowDataCount = totalScsiBytes >= SCSI_XFER_MAX; + if (!useSlowDataCount) + { + scsiSetDataCount(totalScsiBytes); + } + + while ((i < totalSDSectors) && + likely(scsiDev.phase == DATA_IN) && + likely(!scsiDev.resetFlag)) + { + int completedDmaSectors; + if (sdActive && (completedDmaSectors = sdReadDMAPoll(sdActive))) + { + prep += completedDmaSectors; + sdActive -= completedDmaSectors; + } else if (sdActive > 1) + { + if ((scsiDev.data[SD_SECTOR_SIZE * (prep % buffers) + 510] != 0xAA) || + (scsiDev.data[SD_SECTOR_SIZE * (prep % buffers) + 511] != 0x33)) + { + prep += 1; + sdActive -= 1; + } + } + + if (!sdActive && + (prep - i < buffers) && + (prep < totalSDSectors) && + ((totalSDSectors - prep) >= sdPerScsi) && + (likely(!useSlowDataCount) || scsiPhyComplete()) && + (HAL_SD_GetState(&hsd) != HAL_SD_STATE_BUSY)) // rx complete but IRQ not fired yet. + { + // Start an SD transfer if we have space. + uint32_t startBuffer = prep % buffers; + uint32_t sectors = totalSDSectors - prep; + uint32_t freeBuffers = buffers - (prep - i); + + uint32_t contiguousBuffers = buffers - startBuffer; + freeBuffers = freeBuffers < contiguousBuffers + ? freeBuffers : contiguousBuffers; + sectors = sectors < freeBuffers ? sectors : freeBuffers; + + if (sectors > 128) sectors = 128; // 65536 DMA limit !! + + // Round-down when we have odd sector sizes. + if (sdPerScsi != 1) + { + sectors = (sectors / sdPerScsi) * sdPerScsi; + } + + for (int dodgy = 0; dodgy < sectors; dodgy++) + { + scsiDev.data[SD_SECTOR_SIZE * (startBuffer + dodgy) + 510] = 0xAA; + scsiDev.data[SD_SECTOR_SIZE * (startBuffer + dodgy) + 511] = 0x33; + } + + sdReadDMA(sdLBA + prep, sectors, &scsiDev.data[SD_SECTOR_SIZE * startBuffer]); + + sdActive = sectors; + + if (useSlowDataCount) + { + scsiSetDataCount((sectors / sdPerScsi) * bytesPerSector); + } + + // Wait now that the SD card is busy + // Chances are we've probably already waited sufficient time, + // but it's hard to measure microseconds cheaply. So just wait + // extra just-in-case. Hopefully it's in parallel with dma. + if (phaseChangeDelayUs > 0) + { + s2s_delay_us(phaseChangeDelayUs); + phaseChangeDelayUs = 0; + } + } + + if (((prep - i) > 0) && scsiFifoReady()) - { - int dmaBytes = SD_SECTOR_SIZE; - if ((i % sdPerScsi) == (sdPerScsi - 1)) - { - dmaBytes = bytesPerSector % SD_SECTOR_SIZE; - if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE; - } - - uint8_t* scsiDmaData = &(scsiDev.data[SD_SECTOR_SIZE * (i % buffers)]); - scsiWritePIO(scsiDmaData, dmaBytes); - - ++i; - } - } - - if (phaseChangeDelayUs > 0 && !scsiDev.resetFlag) // zero bytes ? - { - s2s_delay_us(phaseChangeDelayUs); - phaseChangeDelayUs = 0; - } - - // Wait for the SD transfer to complete before we disable IRQs. - // (Otherwise some cards will cause an error if we don't sent the - // stop transfer command via the DMA complete handler in time) - while (HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY) - { - // Wait while keeping BSY. - } - - // We've finished transferring the data to the FPGA, now wait until it's - // written to he SCSI bus. - while (!scsiPhyComplete() && - likely(scsiDev.phase == DATA_IN) && - likely(!scsiDev.resetFlag)) - { - __disable_irq(); - if (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) { - __WFI(); + int dmaBytes = SD_SECTOR_SIZE; + if ((i % sdPerScsi) == (sdPerScsi - 1)) + { + dmaBytes = bytesPerSector % SD_SECTOR_SIZE; + if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE; + } + + uint8_t* scsiDmaData = &(scsiDev.data[SD_SECTOR_SIZE * (i % buffers)]); + scsiWritePIO(scsiDmaData, dmaBytes); + + ++i; + } + } + + if (phaseChangeDelayUs > 0 && !scsiDev.resetFlag) // zero bytes ? + { + s2s_delay_us(phaseChangeDelayUs); + phaseChangeDelayUs = 0; + } + + if (scsiDev.resetFlag) + { + HAL_SD_Abort(&hsd); + } + else + { + // Wait for the SD transfer to complete before we disable IRQs. + // (Otherwise some cards will cause an error if we don't sent the + // stop transfer command via the DMA complete handler in time) + while (HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY) + { + // Wait while keeping BSY. } - __enable_irq(); - } - - if (scsiDev.phase == DATA_IN) - { - scsiDev.phase = STATUS; - } - scsiDiskReset(); } - else if (scsiDev.phase == DATA_OUT && - transfer.currentBlock != transfer.blocks) - { - scsiEnterPhase(DATA_OUT); - - const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector); - int totalSDSectors = transfer.blocks * sdPerScsi; - uint32_t sdLBA = - SCSISector2SD( - scsiDev.target->cfg->sdSectorStart, - bytesPerSector, - transfer.lba); - int i = 0; - int clearBSY = 0; + + HAL_SD_CardStateTypeDef cardState = HAL_SD_GetCardState(&hsd); + while (cardState == HAL_SD_CARD_PROGRAMMING || cardState == HAL_SD_CARD_SENDING) + { + cardState = HAL_SD_GetCardState(&hsd); + } + + // We've finished transferring the data to the FPGA, now wait until it's + // written to he SCSI bus. + while (!scsiPhyComplete() && + likely(scsiDev.phase == DATA_IN) && + likely(!scsiDev.resetFlag)) + { + __disable_irq(); + if (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) + { + __WFI(); + } + __enable_irq(); + } + + if (scsiDev.phase == DATA_IN) + { + scsiDev.phase = STATUS; + } + scsiDiskReset(); + } + else if (scsiDev.phase == DATA_OUT && + transfer.currentBlock != transfer.blocks) + { + scsiEnterPhase(DATA_OUT); + + const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector); + int totalSDSectors = transfer.blocks * sdPerScsi; + uint32_t sdLBA = + SCSISector2SD( + scsiDev.target->cfg->sdSectorStart, + bytesPerSector, + transfer.lba); + int i = 0; + int clearBSY = 0; int disconnected = 0; - int parityError = 0; - int enableParity = scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY; - - uint32_t maxSectors = sizeof(scsiDev.data) / SD_SECTOR_SIZE; - - static_assert(SCSI_XFER_MAX >= sizeof(scsiDev.data), "Assumes SCSI_XFER_MAX >= sizeof(scsiDev.data)"); - - // Start reading and filling fifos as soon as possible. - // It's highly unlikely that someone is going to use huge transfers - // per scsi command, but if they do it'll be slower than usual. - // Note: Happens in Macintosh FWB HDD Toolkit benchmarks which default - // to 768kb - uint32_t totalTransferBytes = transfer.blocks * bytesPerSector; - int useSlowDataCount = totalTransferBytes >= SCSI_XFER_MAX; - if (!useSlowDataCount) - { - DWT->CYCCNT = 0; // Start counting cycles - scsiSetDataCount(totalTransferBytes); - } - - while ((i < totalSDSectors) && - likely(scsiDev.phase == DATA_OUT) && - likely(!scsiDev.resetFlag)) - // KEEP GOING to ensure FIFOs are in a good state. - // likely(!parityError || !enableParity)) - { - if (bytesPerSector == SD_SECTOR_SIZE) - { + int parityError = 0; + int enableParity = scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY; + + uint32_t maxSectors = sizeof(scsiDev.data) / SD_SECTOR_SIZE; + + static_assert(SCSI_XFER_MAX >= sizeof(scsiDev.data), "Assumes SCSI_XFER_MAX >= sizeof(scsiDev.data)"); + + // Start reading and filling fifos as soon as possible. + // It's highly unlikely that someone is going to use huge transfers + // per scsi command, but if they do it'll be slower than usual. + // Note: Happens in Macintosh FWB HDD Toolkit benchmarks which default + // to 768kb + uint32_t totalTransferBytes = transfer.blocks * bytesPerSector; + int useSlowDataCount = totalTransferBytes >= SCSI_XFER_MAX; + if (!useSlowDataCount) + { + DWT->CYCCNT = 0; // Start counting cycles + scsiSetDataCount(totalTransferBytes); + } + + int lastWriteSize = 0; + + while ((i < totalSDSectors) && + likely(scsiDev.phase == DATA_OUT) && + likely(!scsiDev.resetFlag)) + // KEEP GOING to ensure FIFOs are in a good state. + // likely(!parityError || !enableParity)) + { + if (bytesPerSector == SD_SECTOR_SIZE) + { uint32_t maxXferSectors = SCSI_XFER_MAX / SD_SECTOR_SIZE; - uint32_t rem = totalSDSectors - i; - uint32_t sectors = rem < maxXferSectors ? rem : maxXferSectors; + uint32_t rem = totalSDSectors - i; + uint32_t sectors = rem < maxXferSectors ? rem : maxXferSectors; - uint32_t totalBytes = sectors * SD_SECTOR_SIZE; + uint32_t totalBytes = sectors * SD_SECTOR_SIZE; - if (useSlowDataCount) - { - scsiSetDataCount(totalBytes); - } + if (useSlowDataCount) + { + scsiSetDataCount(totalBytes); + } - HAL_SD_WriteBlocks_DMA(&hsd, i + sdLBA, sectors); + lastWriteSize = sectors; + HAL_SD_WriteBlocks_DMA(&hsd, i + sdLBA, sectors); int j = 0; int prep = 0; int sdActive = 0; - uint32_t dmaFinishTime = 0; + uint32_t dmaFinishTime = 0; while (j < sectors && !scsiDev.resetFlag) { if (sdActive && @@ -780,45 +796,45 @@ void scsiDiskPoll() sdActive = 0; } } - if (!sdActive && ((prep - j) > 0)) - { - // Start an SD transfer if we have space. - HAL_SD_WriteBlocks_Data(&hsd, &scsiDev.data[SD_SECTOR_SIZE * (j % maxSectors)]); + if (!sdActive && ((prep - j) > 0)) + { + // Start an SD transfer if we have space. + HAL_SD_WriteBlocks_Data(&hsd, &scsiDev.data[SD_SECTOR_SIZE * (j % maxSectors)]); - sdActive = 1; - } + sdActive = 1; + } if (((prep - j) < maxSectors) && (prep < sectors) && scsiFifoReady()) { - scsiReadPIO( - &scsiDev.data[(prep % maxSectors) * SD_SECTOR_SIZE], - SD_SECTOR_SIZE, - &parityError); + scsiReadPIO( + &scsiDev.data[(prep % maxSectors) * SD_SECTOR_SIZE], + SD_SECTOR_SIZE, + &parityError); prep++; if (prep == sectors) { - dmaFinishTime = s2s_getTime_ms(); + dmaFinishTime = s2s_getTime_ms(); } } - + if (i + prep >= totalSDSectors && !disconnected && - (!parityError || !enableParity) && + (!parityError || !enableParity) && s2s_elapsedTime_ms(dmaFinishTime) >= 180) - { - // 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't. - // Just pretend we're finished. - process_Status(); - clearBSY = process_MessageIn(0); // Will go to BUS_FREE state but keep BSY asserted. + { + // 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't. + // Just pretend we're finished. + process_Status(); + clearBSY = process_MessageIn(0); // Will go to BUS_FREE state but keep BSY asserted. disconnected = 1; - } + } } if (scsiDev.resetFlag) @@ -828,12 +844,15 @@ void scsiDiskPoll() else { while (HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY) {} // Waits for DMA to complete - SDMMC_CmdStopTransfer(hsd.Instance); + if (lastWriteSize > 1) + { + SDMMC_CmdStopTransfer(hsd.Instance); + } } HAL_SD_CardStateTypeDef cardState = HAL_SD_GetCardState(&hsd); while ((cardState == HAL_SD_CARD_PROGRAMMING || cardState == HAL_SD_CARD_RECEIVING) && - s2s_elapsedTime_ms(dmaFinishTime) < 180) + s2s_elapsedTime_ms(dmaFinishTime) < 180) { // Wait while the SD card is writing buffer to flash // The card may remain in the RECEIVING state (even though it's programming) if @@ -841,20 +860,20 @@ void scsiDiskPoll() cardState = HAL_SD_GetCardState(&hsd); } - if (!disconnected && + if (!disconnected && i + sectors >= totalSDSectors && - (!parityError || !enableParity)) - { - // 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't. - // Just pretend we're finished. - process_Status(); - clearBSY = process_MessageIn(0); // Will go to BUS_FREE state but keep BSY asserted. - } + (!parityError || !enableParity)) + { + // 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't. + // Just pretend we're finished. + process_Status(); + clearBSY = process_MessageIn(0); // Will go to BUS_FREE state but keep BSY asserted. + } cardState = HAL_SD_GetCardState(&hsd); while (cardState == HAL_SD_CARD_PROGRAMMING || cardState == HAL_SD_CARD_RECEIVING) @@ -865,99 +884,99 @@ void scsiDiskPoll() cardState = HAL_SD_GetCardState(&hsd); } - i += sectors; - } - else - { - // Well, until we have some proper non-blocking SD code, we must - // do this in a half-duplex fashion. We need to write as much as - // possible in each SD card transaction. - // use sg_dd from sg_utils3 tools to test. - - uint32_t rem = totalSDSectors - i; - uint32_t sectors = rem < maxSectors ? rem : maxSectors; - - if (useSlowDataCount) - { - scsiSetDataCount(sectors * bytesPerSector); - } - - for (int scsiSector = i; scsiSector < i + sectors; ++scsiSector) - { - int dmaBytes = SD_SECTOR_SIZE; - if ((scsiSector % sdPerScsi) == (sdPerScsi - 1)) - { - dmaBytes = bytesPerSector % SD_SECTOR_SIZE; - if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE; - } - - scsiReadPIO(&scsiDev.data[SD_SECTOR_SIZE * (scsiSector - i)], dmaBytes, &parityError); - } - if (!parityError || !enableParity) - { - BSP_SD_WriteBlocks_DMA(&scsiDev.data[0], i + sdLBA, sectors); - } - i += sectors; - } - } - - // Should already be complete here as we've ready the FIFOs - // by now. Check anyway. - __disable_irq(); - while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) - { - __WFI(); - } - __enable_irq(); - - if (clearBSY) - { - enter_BusFree(); - } - - if (scsiDev.phase == DATA_OUT) - { - if (parityError && - (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY)) - { - scsiDev.target->sense.code = ABORTED_COMMAND; - scsiDev.target->sense.asc = SCSI_PARITY_ERROR; - scsiDev.status = CHECK_CONDITION;; - } - scsiDev.phase = STATUS; - } - scsiDiskReset(); - } + i += sectors; + } + else + { + // Well, until we have some proper non-blocking SD code, we must + // do this in a half-duplex fashion. We need to write as much as + // possible in each SD card transaction. + // use sg_dd from sg_utils3 tools to test. + + uint32_t rem = totalSDSectors - i; + uint32_t sectors = rem < maxSectors ? rem : maxSectors; + + if (useSlowDataCount) + { + scsiSetDataCount(sectors * bytesPerSector); + } + + for (int scsiSector = i; scsiSector < i + sectors; ++scsiSector) + { + int dmaBytes = SD_SECTOR_SIZE; + if ((scsiSector % sdPerScsi) == (sdPerScsi - 1)) + { + dmaBytes = bytesPerSector % SD_SECTOR_SIZE; + if (dmaBytes == 0) dmaBytes = SD_SECTOR_SIZE; + } + + scsiReadPIO(&scsiDev.data[SD_SECTOR_SIZE * (scsiSector - i)], dmaBytes, &parityError); + } + if (!parityError || !enableParity) + { + BSP_SD_WriteBlocks_DMA(&scsiDev.data[0], i + sdLBA, sectors); + } + i += sectors; + } + } + + // Should already be complete here as we've ready the FIFOs + // by now. Check anyway. + __disable_irq(); + while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) + { + __WFI(); + } + __enable_irq(); + + if (clearBSY) + { + enter_BusFree(); + } + + if (scsiDev.phase == DATA_OUT) + { + if (parityError && + (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY)) + { + scsiDev.target->sense.code = ABORTED_COMMAND; + scsiDev.target->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! + 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 0 - if ( - ((scsiDev.boardCfg.flags & S2S_CFG_ENABLE_CACHE) == 0) || - (transfer.multiBlock == 0) - ) + if ( + ((scsiDev.boardCfg.flags & S2S_CFG_ENABLE_CACHE) == 0) || + (transfer.multiBlock == 0) + ) #endif - { - sdCompleteTransfer(); - } + { + sdCompleteTransfer(); + } - transfer.multiBlock = 0; + transfer.multiBlock = 0; } void scsiDiskInit() { - scsiDiskReset(); + scsiDiskReset(); - // Don't require the host to send us a START STOP UNIT command - blockDev.state = DISK_STARTED; + // Don't require the host to send us a START STOP UNIT command + blockDev.state = DISK_STARTED; } diff --git a/src/firmware/main.c b/src/firmware/main.c index caea8e20..c7dd5542 100755 --- a/src/firmware/main.c +++ b/src/firmware/main.c @@ -1,19 +1,19 @@ -// Copyright (C) 2014 Michael McMaster +// Copyright (C) 2014 Michael McMaster // -// This file is part of SCSI2SD. +// 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 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. +// 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 . +// You should have received a copy of the GNU General Public License +// along with SCSI2SD. If not, see . #ifdef STM32F2xx #include "stm32f2xx.h" @@ -38,6 +38,8 @@ #include "usb_device/usbd_composite.h" #include "usb_device/usbd_msc_storage_sd.h" +#include "bsp_driver_sd.h" + const char* Notice = "Copyright (C) 2020 Michael McMaster "; uint32_t lastSDPoll; @@ -47,107 +49,132 @@ static int isUsbStarted; void mainEarlyInit() { #ifdef nULPI_RESET_GPIO_Port - // Disable the ULPI chip - HAL_GPIO_WritePin(nULPI_RESET_GPIO_Port, nULPI_RESET_Pin, GPIO_PIN_RESET); + // Disable the ULPI chip + HAL_GPIO_WritePin(nULPI_RESET_GPIO_Port, nULPI_RESET_Pin, GPIO_PIN_RESET); #endif - // Sets up function pointers only - s2s_initUsbDeviceStorage(); + // Sets up function pointers only + s2s_initUsbDeviceStorage(); } void mainInit() { - s2s_timeInit(); - s2s_checkHwVersion(); - - s2s_ledInit(); - s2s_fpgaInit(); - - scsiPhyInit(); - - scsiDiskInit(); - sdInit(); - s2s_configInit(&scsiDev.boardCfg); - scsiPhyConfig(); - scsiInit(); - - #ifdef S2S_USB_HS - // Enable the ULPI chip - HAL_GPIO_WritePin(nULPI_RESET_GPIO_Port, nULPI_RESET_Pin, GPIO_PIN_SET); - s2s_delay_ms(5); - #endif - - MX_USB_DEVICE_Init(); // USB lun config now available. - isUsbStarted = 1; - - // Optional bootup delay - int delaySeconds = 0; - while (delaySeconds < scsiDev.boardCfg.startupDelay) { - // Keep the USB connection working, otherwise it's very hard to revert - // silly extra-long startup delay settings. - int i; - for (i = 0; i < 200; i++) { - s2s_delay_ms(5); - scsiDev.watchdogTick++; - s2s_configPoll(); - } - ++delaySeconds; - } - - lastSDPoll = s2s_getTime_ms(); + s2s_timeInit(); + s2s_checkHwVersion(); + + s2s_ledInit(); + s2s_fpgaInit(); + + scsiPhyInit(); + + scsiDiskInit(); + sdInit(); + s2s_configInit(&scsiDev.boardCfg); + scsiPhyConfig(); + scsiInit(); + + #ifdef S2S_USB_HS + // Enable the ULPI chip + HAL_GPIO_WritePin(nULPI_RESET_GPIO_Port, nULPI_RESET_Pin, GPIO_PIN_SET); + s2s_delay_ms(5); + #endif + + MX_USB_DEVICE_Init(); // USB lun config now available. + isUsbStarted = 1; + + // Optional bootup delay + int delaySeconds = 0; + while (delaySeconds < scsiDev.boardCfg.startupDelay) { + // Keep the USB connection working, otherwise it's very hard to revert + // silly extra-long startup delay settings. + int i; + for (i = 0; i < 200; i++) { + s2s_delay_ms(5); + scsiDev.watchdogTick++; + s2s_configPoll(); + } + ++delaySeconds; + } + +#ifdef TEST_SD_CARDS + s2s_ledOn(); + for (int h = 0; h < 1000; ++h) + { + uint8_t g = BSP_SD_ReadBlocks_DMA(scsiDev.data, (1000 - h) * 1000, 64); + if (g != MSD_OK) + { + while (1) {} + } + if (h & 1) + { + uint8_t r = BSP_SD_ReadBlocks_DMA(scsiDev.data, h * 1000, 1); + if (r != MSD_OK) + { + while (1) {} + } + } + else + { + BSP_SD_WriteBlocks_DMA(scsiDev.data, h * 2000, 1); + } + } + s2s_ledOff(); +#endif + + lastSDPoll = s2s_getTime_ms(); } void mainLoop() { - scsiDev.watchdogTick++; + scsiDev.watchdogTick++; - scsiPoll(); - scsiDiskPoll(); - s2s_configPoll(); + scsiPoll(); + scsiDiskPoll(); + s2s_configPoll(); #ifdef S2S_USB_FS - int usbBusy = s2s_usbDevicePoll(&hUsbDeviceFS); + int usbBusy = s2s_usbDevicePoll(&hUsbDeviceFS); #endif #ifdef S2S_USB_HS - int usbBusy = s2s_usbDevicePoll(&hUsbDeviceHS); + int usbBusy = s2s_usbDevicePoll(&hUsbDeviceHS); #endif #if 0 - sdPoll(); + sdPoll(); #endif // TODO test if USB transfer is in progress - if (unlikely(scsiDev.phase == BUS_FREE) && !usbBusy) - { - if (unlikely(s2s_elapsedTime_ms(lastSDPoll) > 200)) - { - lastSDPoll = s2s_getTime_ms(); - if (sdInit()) - { - s2s_configInit(&scsiDev.boardCfg); - scsiPhyConfig(); - scsiInit(); - - if (isUsbStarted) - { + if (unlikely(scsiDev.phase == BUS_FREE) && !usbBusy) + { + if (unlikely(s2s_elapsedTime_ms(lastSDPoll) > 200)) + { + lastSDPoll = s2s_getTime_ms(); + if (sdInit()) + { + s2s_configInit(&scsiDev.boardCfg); + scsiPhyConfig(); + scsiInit(); + + if (isUsbStarted) + { #ifdef S2S_USB_FS - USBD_Stop(&hUsbDeviceFS); - s2s_delay_ms(128); - USBD_Start(&hUsbDeviceFS); + USBD_Stop(&hUsbDeviceFS); + s2s_delay_ms(128); + USBD_Start(&hUsbDeviceFS); #endif #ifdef S2S_USB_HS - USBD_Stop(&hUsbDeviceHS); - s2s_delay_ms(128); - USBD_Start(&hUsbDeviceHS); + USBD_Stop(&hUsbDeviceHS); + s2s_delay_ms(128); + USBD_Start(&hUsbDeviceHS); #endif - } - } - } - } - else if (usbBusy || ((scsiDev.phase >= 0) && (blockDev.state & DISK_PRESENT))) - { - // don't waste time scanning SD cards while we're doing disk IO - lastSDPoll = s2s_getTime_ms(); - } + } + } + } + } + else if (usbBusy || ((scsiDev.phase >= 0) && (blockDev.state & DISK_PRESENT))) + { + // don't waste time scanning SD cards while we're doing disk IO + lastSDPoll = s2s_getTime_ms(); + } } -- 2.38.5