| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195 | //    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/>.#ifdef STM32F2xx#include "stm32f2xx.h"#endif#ifdef STM32F4xx#include "stm32f4xx.h"#endif#include <assert.h>// For SD write direct routines#include "sdio.h"#include "bsp_driver_sd.h"#include "scsi.h"#include "scsiPhy.h"#include "config.h"#include "disk.h"#include "sd.h"#include "time.h"#include "bsp.h"#include <string.h>// GlobalBlockDevice blockDev;Transfer transfer;static int doSdInit(){    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;}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"        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;    }}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;    }}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);        }    }}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    {        if (unlikely(scsiDev.target->cfg->deviceType == S2S_CFG_FLOPPY_14MB) ||            scsiDev.compatMode < COMPAT_SCSI2)        {            s2s_delay_ms(10);        }        else        {            s2s_delay_us(10);        }    }}static int doTestUnitReady(){    int ready = 1;    if (likely(blockDev.state == (DISK_PRESENT | DISK_INITIALISED) &&		scsiDev.target->started))    {        // nothing to do.    }    else if (unlikely(!scsiDev.target->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 commandsint scsiDiskCommand(){    int commandHandled = 1;    uint8_t command = scsiDev.cdb[0];    if (unlikely(command == 0x1B))    {        // START STOP UNIT        // Enable or disable media access operations.        //int immed = scsiDev.cdb[1] & 1;        int start = scsiDev.cdb[4] & 1;	int loadEject = scsiDev.cdb[4] & 2;	        if (loadEject)        {            // Ignore load/eject requests. We can't do that.        }        else if (start)        {            scsiDev.target->started = 1;            if (!(blockDev.state & DISK_INITIALISED))            {                doSdInit();            }        }        else        {            scsiDev.target->started = 0;        }    }    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;}static void diskDataInBuffered(int totalSDSectors, uint32_t sdLBA, int useSlowDataCount, uint32_t* phaseChangeDelayNs){    uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;    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;    int gotHalf = 0;    int sentHalf = 0;    while ((i < totalSDSectors) &&        likely(scsiDev.phase == DATA_IN) &&        likely(!scsiDev.resetFlag))    {        int completedDmaSectors;        if (sdActive && (completedDmaSectors = sdReadDMAPoll(sdActive)))        {            prep += completedDmaSectors;            sdActive -= completedDmaSectors;            gotHalf = 0;        }        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;                gotHalf = 0;            }            else if (scsiDev.data[SD_SECTOR_SIZE * (prep % buffers) + 127] != 0xAA)            {                // Half-block                gotHalf = 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) + 127] = 0xAA;                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 (*phaseChangeDelayNs > 0)            {                s2s_delay_ns(*phaseChangeDelayNs);                *phaseChangeDelayNs = 0;            }        }        int fifoReady = scsiFifoReady();        if (((prep - i) > 0) && fifoReady)        {            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)]);            if (sentHalf)            {                scsiDmaData += SD_SECTOR_SIZE / 2;                dmaBytes -= (SD_SECTOR_SIZE / 2);            }            scsiWritePIO(scsiDmaData, dmaBytes);            ++i;            sentHalf = 0;            gotHalf = 0;        }        else if (gotHalf && !sentHalf && fifoReady && bytesPerSector == SD_SECTOR_SIZE)        {            uint8_t* scsiDmaData = &(scsiDev.data[SD_SECTOR_SIZE * (i % buffers)]);            scsiWritePIO(scsiDmaData, SD_SECTOR_SIZE / 2);            sentHalf = 1;        }    }}// Transfer from the SD card straight to the SCSI Fifo without storing in memory first for lower latency// This requires hardware flow control on the SD device (broken on stm32f205)// Only functional for 512 byte sectors.static void diskDataInDirect(uint32_t totalSDSectors, uint32_t sdLBA, int useSlowDataCount, uint32_t* phaseChangeDelayNs){    sdReadCmd(sdLBA, totalSDSectors);    // Wait while the SD card starts buffering data    if (*phaseChangeDelayNs > 0)    {        s2s_delay_ns(*phaseChangeDelayNs);        *phaseChangeDelayNs = 0;    }    for (int i = 0; i < totalSDSectors && !scsiDev.resetFlag; ++i)    {        if (i % 128 == 0)        {            // SD DPSM has 24 bit limit. Re-use 128 (DMA limit)            uint32_t chunk = totalSDSectors - i > 128 ? 128 : totalSDSectors - i;            sdReadPIOData(chunk);            if (useSlowDataCount)            {                while (!scsiDev.resetFlag && !scsiPhyComplete())                {}                scsiSetDataCount(chunk * SD_SECTOR_SIZE); // SCSI_XFER_MAX > 65536            }        }        // The SCSI fifo is a full sector so we only need to check once.        while (!scsiFifoReady() && !scsiDev.resetFlag)        {}        int byteCount = 0;        while(byteCount < SD_SECTOR_SIZE &&            likely(!scsiDev.resetFlag) &&            likely(scsiDev.phase == DATA_IN) &&            !__HAL_SD_GET_FLAG(&hsd, SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT))        {            if(__HAL_SD_GET_FLAG(&hsd, SDIO_FLAG_RXFIFOHF))            {                // The SDIO fifo is 32 x 32bits. As we're using the "half full" flag we must                // always read half the FIFO.                for (int j = 0; j < 4; ++j)                {                    uint32_t data[4];                    data[0] = SDIO_ReadFIFO(hsd.Instance);                    data[1] = SDIO_ReadFIFO(hsd.Instance);                    data[2] = SDIO_ReadFIFO(hsd.Instance);                    data[3] = SDIO_ReadFIFO(hsd.Instance);                    *((volatile uint32_t*)SCSI_FIFO_DATA) = data[0];                    *((volatile uint32_t*)SCSI_FIFO_DATA) = data[1];                    *((volatile uint32_t*)SCSI_FIFO_DATA) = data[2];                    *((volatile uint32_t*)SCSI_FIFO_DATA) = data[3];                }                byteCount += 64;            }        }        int error = 0;        if (__HAL_SD_GET_FLAG(&hsd, SDIO_FLAG_DTIMEOUT))        {            __HAL_SD_CLEAR_FLAG(&hsd, SDIO_FLAG_DTIMEOUT);            error = 1;        }        else if (__HAL_SD_GET_FLAG(&hsd, SDIO_FLAG_DCRCFAIL))        {            __HAL_SD_CLEAR_FLAG(&hsd, SDIO_FLAG_DCRCFAIL);            error = 1;        }        else if (__HAL_SD_GET_FLAG(&hsd, SDIO_FLAG_RXOVERR))        {            __HAL_SD_CLEAR_FLAG(&hsd, SDIO_FLAG_RXOVERR);            error = 1;        }        if (error && scsiDev.phase == DATA_IN)        {            __HAL_SD_CLEAR_FLAG(&hsd, SDIO_STATIC_FLAGS);            scsiDiskReset();            scsiDev.status = CHECK_CONDITION;            scsiDev.target->sense.code = HARDWARE_ERROR;            scsiDev.target->sense.asc = LOGICAL_UNIT_COMMUNICATION_FAILURE;            scsiDev.phase = STATUS;        }        // We need the SCSI FIFO count to complete even after the SD read has failed        while (byteCount < SD_SECTOR_SIZE &&            likely(!scsiDev.resetFlag))        {            scsiPhyTx32(0, 0);            byteCount += 4;        }    }    /* Send stop transmission command in case of multiblock read */    if(totalSDSectors > 1U)    {        SDMMC_CmdStopTransfer(hsd.Instance);    }    // Read remaining data    uint32_t extraCount = SD_DATATIMEOUT;    while ((__HAL_SD_GET_FLAG(&hsd, SDIO_FLAG_RXDAVL)) && (extraCount > 0))    {        SDIO_ReadFIFO(hsd.Instance);        extraCount--;    }    __HAL_SD_CLEAR_FLAG(&hsd, SDIO_STATIC_DATA_FLAGS);    hsd.State = HAL_SD_STATE_READY;        sdCompleteTransfer(); // Probably overkill}static void diskDataIn(){    uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;    // Take responsibility for waiting for the phase delays    uint32_t phaseChangeDelayNs = scsiEnterPhaseImmediate(DATA_IN);    int totalSDSectors =        transfer.blocks * SDSectorsPerSCSISector(bytesPerSector);    uint32_t sdLBA =        SCSISector2SD(            scsiDev.target->cfg->sdSectorStart,            bytesPerSector,            transfer.lba);    // 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);    }#ifdef STM32F4xx    // Direct mode requires hardware flow control to be working on the SD peripheral    // Code isn't currently working above 128 sectors. TODO investigate    if (totalSDSectors < 128 && bytesPerSector == SD_SECTOR_SIZE)    {        diskDataInDirect(totalSDSectors, sdLBA, useSlowDataCount, &phaseChangeDelayNs);    }    else#endif     {        diskDataInBuffered(totalSDSectors, sdLBA, useSlowDataCount, &phaseChangeDelayNs);    }    if (phaseChangeDelayNs > 0 && !scsiDev.resetFlag) // zero bytes ?    {        s2s_delay_ns(phaseChangeDelayNs);        phaseChangeDelayNs = 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.        }    }    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))    {        // spin    }    if (scsiDev.phase == DATA_IN)    {        scsiDev.phase = STATUS;    }    scsiDiskReset();}void diskDataOut_512(int totalSDSectors, uint32_t sdLBA, int useSlowDataCount, int* clearBSY, int* parityError){    int i = 0;    int disconnected = 0;    int enableParity = scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY;    uint32_t maxSectors = sizeof(scsiDev.data) / SD_SECTOR_SIZE;    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))    {        uint32_t maxXferSectors = SCSI_XFER_MAX / SD_SECTOR_SIZE;        uint32_t rem = totalSDSectors - i;        uint32_t sectors = rem < maxXferSectors ? rem : maxXferSectors;        uint32_t totalBytes = sectors * SD_SECTOR_SIZE;        if (useSlowDataCount)        {            scsiSetDataCount(totalBytes);        }        lastWriteSize = sectors;        HAL_SD_WriteBlocks_DMA(&hsd, i + sdLBA, sectors);        int j = 0;        int prep = 0;        int sdActive = 0;        uint32_t dmaFinishTime = 0;        while (j < sectors && !scsiDev.resetFlag)        {            if (sdActive &&                HAL_SD_GetState(&hsd) != HAL_SD_STATE_BUSY &&                !sdIsBusy())            {                j += sdActive;                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)]);                sdActive = 1;            }            if (((prep - j) < maxSectors) &&                (prep < sectors) &&                scsiFifoReady())            {                scsiReadPIO(                    &scsiDev.data[(prep % maxSectors) * SD_SECTOR_SIZE],                    SD_SECTOR_SIZE,                    parityError);                prep++;                if (prep == sectors)                {                    dmaFinishTime = s2s_getTime_ms();                }            }                    if (i + prep >= totalSDSectors &&                !disconnected &&                (!(*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.                disconnected = 1;            }        }        if (scsiDev.resetFlag)        {            HAL_SD_Abort(&hsd);        }        else        {            while (HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY) {} // Waits for DMA to complete            if (lastWriteSize > 1)            {                SDMMC_CmdStopTransfer(hsd.Instance);            }        }        while (sdIsBusy() &&            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            // it has buffer space to receive more data available.        }        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.        }        // Wait while the SD card is writing buffer to flash        // The card may remain in the RECEIVING state (even though it's programming) if        // it has buffer space to receive more data available.        while (sdIsBusy()) {}        HAL_SD_CardStateTypeDef cardState = HAL_SD_GetCardState(&hsd);        while (cardState == HAL_SD_CARD_PROGRAMMING || cardState == HAL_SD_CARD_RECEIVING)         {            // Wait while the SD card is writing buffer to flash            // The card may remain in the RECEIVING state (even though it's programming) if            // it has buffer space to receive more data available.            cardState = HAL_SD_GetCardState(&hsd);        }        i += sectors;       }}void diskDataOut_variableSectorSize(int sdPerScsi, int totalSDSectors, uint32_t sdLBA, int useSlowDataCount, int* parityError){    uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;    int i = 0;    int enableParity = scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY;    uint32_t maxSectors = sizeof(scsiDev.data) / SD_SECTOR_SIZE;    while ((i < totalSDSectors) &&        likely(scsiDev.phase == DATA_OUT) &&        likely(!scsiDev.resetFlag))        // KEEP GOING to ensure FIFOs are in a good state.        // likely(!parityError || !enableParity))    {        // 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;        if (rem <= maxSectors)        {            sectors = rem;        }        else        {            sectors = maxSectors;            while (sectors % sdPerScsi) sectors--;        }                if (useSlowDataCount)        {            scsiSetDataCount((sectors / sdPerScsi) * 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;    }}void diskDataOut(){    uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;    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 clearBSY = 0;    int parityError = 0;    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);    }    if (bytesPerSector == SD_SECTOR_SIZE)    {        diskDataOut_512(totalSDSectors, sdLBA, useSlowDataCount, &clearBSY, &parityError);    }    else    {        diskDataOut_variableSectorSize(sdPerScsi, totalSDSectors, sdLBA, useSlowDataCount, &parityError);    }        // Should already be complete here as we've ready the FIFOs    // by now. Check anyway.    while (!scsiPhyComplete() && likely(!scsiDev.resetFlag))    {        // spin    }    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 scsiDiskPoll(){    if (scsiDev.phase == DATA_IN &&        transfer.currentBlock != transfer.blocks)    {        diskDataIn();     }    else if (scsiDev.phase == DATA_OUT &&        transfer.currentBlock != transfer.blocks)    {        diskDataOut();    }}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 0    if (        ((scsiDev.boardCfg.flags & S2S_CFG_ENABLE_CACHE) == 0) ||            (transfer.multiBlock == 0)        )#endif    {        sdCompleteTransfer();    }    transfer.multiBlock = 0;}void scsiDiskInit(){    scsiDiskReset();}
 |