From d33b3ceb7298cbd90ac680ef9dbfd604bd71d11b Mon Sep 17 00:00:00 2001 From: Michael McMaster Date: Thu, 16 Jun 2016 21:21:00 +1000 Subject: [PATCH] SD hotswap, SD fixes, SCSI interface fixes, performance improvements. --- CHANGELOG | 6 + .../Src/stm32f2xx_hal_sd.c | 1 - STM32CubeMX/SCSI2SD-V6/Src/sdio.c | 6 +- doc/SCSI2SD_QuickStartGuide.odt | Bin 17491 -> 17368 bytes include/scsi2sd.h | 7 +- rtl/fpga_bitmap.o | Bin 32724 -> 32724 bytes src/firmware/config.c | 77 +++++-- src/firmware/disk.c | 23 +- src/firmware/main.c | 31 ++- src/firmware/scsi.c | 14 +- src/firmware/scsiPhy.c | 59 ++++- src/firmware/scsiPhy.h | 8 +- src/firmware/sd.c | 201 ++++++------------ src/firmware/sd.h | 1 - src/firmware/usb_device/usbd_msc_storage_sd.c | 12 +- 15 files changed, 251 insertions(+), 195 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 28485201..03414da6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,2 +1,8 @@ +20160616 6.01 + - Improved SD card compatibility + - Fixed SCSI interfaces on slower SCSI controllers + - Significant performance improvements + - Added SD card hotswap support. + 20160528 6.0 - First BETA firmware for the 6.0 hardware version of the SCSI2SD. diff --git a/STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_sd.c b/STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_sd.c index 399b7de6..418163e4 100755 --- a/STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_sd.c +++ b/STM32CubeMX/SCSI2SD-V6/Drivers/STM32F2xx_HAL_Driver/Src/stm32f2xx_hal_sd.c @@ -178,7 +178,6 @@ /* Includes ------------------------------------------------------------------*/ #include "stm32f2xx_hal.h" - #ifdef HAL_SD_MODULE_ENABLED /** @addtogroup STM32F2xx_HAL_Driver diff --git a/STM32CubeMX/SCSI2SD-V6/Src/sdio.c b/STM32CubeMX/SCSI2SD-V6/Src/sdio.c index 49b4de96..d1bb9dca 100755 --- a/STM32CubeMX/SCSI2SD-V6/Src/sdio.c +++ b/STM32CubeMX/SCSI2SD-V6/Src/sdio.c @@ -84,14 +84,16 @@ void HAL_SD_MspInit(SD_HandleTypeDef* hsd) GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 |GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; + //GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Pull = GPIO_PULLUP; // MM GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = GPIO_AF12_SDIO; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_2; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; + //GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Pull = GPIO_PULLUP; // MM GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_InitStruct.Alternate = GPIO_AF12_SDIO; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); diff --git a/doc/SCSI2SD_QuickStartGuide.odt b/doc/SCSI2SD_QuickStartGuide.odt index 61b1bba2ebd5497379380f716828464d3b3010ee..9dbcb0ffed04bf579e06e2dd170d94c68fa25ce8 100644 GIT binary patch delta 6657 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 YOBoWla#65^7y(#R1{0A`{AoS@2TGW8F#rGn delta 6896 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

BGjQOueFo&|I~^sz1Q@;y3lIlg%i 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 -static const uint16_t FIRMWARE_VERSION = 0x0600; +static const uint16_t FIRMWARE_VERSION = 0x0601; // 1 flash row static const uint8_t DEFAULT_CONFIG[128] = @@ -68,19 +68,6 @@ static int usbDebugEpState; #endif static int usbReady; // TODO MM REMOVE. Unused ? -static void initS2S_BoardCfg(S2S_BoardCfg* config) { - if (memcmp(config->magic, "BCFG", 4)) { - config->selectionDelay = 255; // auto - config->flags6 = S2S_CFG_ENABLE_TERMINATOR; - - memcpy( - s2s_cfg + sizeof(S2S_BoardCfg), - DEFAULT_CONFIG, - sizeof(S2S_TargetCfg)); - } -} - - void s2s_configInit(S2S_BoardCfg* config) { @@ -88,7 +75,7 @@ void s2s_configInit(S2S_BoardCfg* config) usbReady = 0; // We don't know if host is connected yet. - if (blockDev.state & DISK_PRESENT && sdDev.capacity) + if ((blockDev.state & DISK_PRESENT) && sdDev.capacity) { int cfgSectors = (S2S_CFG_SIZE + 511) / 512; BSP_SD_ReadBlocks_DMA( @@ -98,11 +85,35 @@ void s2s_configInit(S2S_BoardCfg* config) cfgSectors); memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg)); - } - initS2S_BoardCfg(config); + if (memcmp(config->magic, "BCFG", 4)) + { + // Invalid SD card config, use default. + memset(&s2s_cfg[0], 0, S2S_CFG_SIZE); + memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg)); + memcpy(config->magic, "BCFG", 4); + config->selectionDelay = 255; // auto + config->flags6 = S2S_CFG_ENABLE_TERMINATOR; + + memcpy( + &s2s_cfg[0] + sizeof(S2S_BoardCfg), + DEFAULT_CONFIG, + sizeof(S2S_TargetCfg)); + } + } + else + { + // No SD card, use existing config if valid + if (memcmp(config->magic, "BCFG", 4)) + { + // Not valid, use empty config with no disks. + memset(&s2s_cfg[0], 0, S2S_CFG_SIZE); + memcpy(config, s2s_cfg, sizeof(S2S_BoardCfg)); + config->selectionDelay = 255; // auto + config->flags6 = S2S_CFG_ENABLE_TERMINATOR; + } + } - scsiPhyConfig(); } @@ -154,6 +165,32 @@ scsiDevInfoCommand() hidPacket_send(response, sizeof(response)); } +static void +debugCommand() +{ + uint8_t response[32]; + memcpy(&response, &scsiDev.cdb, 12); + response[12] = scsiDev.msgIn; + response[13] = scsiDev.msgOut; + response[14] = scsiDev.lastStatus; + response[15] = scsiDev.lastSense; + response[16] = scsiDev.phase; + response[17] = scsiStatusBSY(); + response[18] = *SCSI_STS_SELECTED; + response[19] = scsiStatusATN(); + response[20] = scsiStatusRST(); + response[21] = scsiDev.rstCount; + response[22] = scsiDev.selCount; + response[23] = scsiDev.msgCount; + response[24] = scsiDev.cmdCount; + response[25] = scsiDev.watchdogTick; + response[26] = blockDev.state; + response[27] = scsiDev.lastSenseASC >> 8; + response[28] = scsiDev.lastSenseASC; + response[29] = *SCSI_STS_DBX; + response[30] = LastTrace; + hidPacket_send(response, sizeof(response)); +} static void sdWriteCommand(const uint8_t* cmd, size_t cmdSize) @@ -230,6 +267,10 @@ processCommand(const uint8_t* cmd, size_t cmdSize) sdReadCommand(cmd, cmdSize); break; + case S2S_CMD_DEBUG: + debugCommand(); + break; + case S2S_CMD_NONE: // invalid default: break; diff --git a/src/firmware/disk.c b/src/firmware/disk.c index 886bfe25..d4c826b7 100755 --- a/src/firmware/disk.c +++ b/src/firmware/disk.c @@ -35,12 +35,7 @@ static int doSdInit() int result = 0; if (blockDev.state & DISK_PRESENT) { - result = sdInit(); - - if (result) - { - blockDev.state = blockDev.state | DISK_INITIALISED; - } + blockDev.state = blockDev.state | DISK_INITIALISED; } return result; } @@ -628,7 +623,6 @@ void scsiDiskPoll() scsiWriteDMA(&scsiDev.data[SD_SECTOR_SIZE * (i % buffers)], dmaBytes); scsiActive = 1; } - } // We've finished transferring the data to the FPGA, now wait until it's @@ -656,6 +650,11 @@ void scsiDiskPoll() const int sdPerScsi = SDSectorsPerSCSISector(bytesPerSector); int totalSDSectors = transfer.blocks * sdPerScsi; + uint32_t sdLBA = + SCSISector2SD( + scsiDev.target->cfg->sdSectorStart, + bytesPerSector, + transfer.lba); // int buffers = sizeof(scsiDev.data) / SD_SECTOR_SIZE; // int prep = 0; int i = 0; @@ -677,7 +676,7 @@ void scsiDiskPoll() uint32_t sectors = totalSDSectors < maxSectors ? totalSDSectors : maxSectors; scsiRead(&scsiDev.data[0], sectors * SD_SECTOR_SIZE); - sdTmpWrite(&scsiDev.data[0], i + transfer.lba, sectors); + sdTmpWrite(&scsiDev.data[0], i + sdLBA, sectors); i += sectors; #if 0 // Wait for the next DMA interrupt. It's beneficial to halt the @@ -863,13 +862,5 @@ void scsiDiskInit() // Don't require the host to send us a START STOP UNIT command blockDev.state = DISK_STARTED; - // WP pin not available for micro-sd - // TODO read card WP register - #if 0 - if (SD_WP_Read()) - { - blockDev.state = blockDev.state | DISK_WP; - } - #endif } diff --git a/src/firmware/main.c b/src/firmware/main.c index 04d2c3b0..a2317626 100755 --- a/src/firmware/main.c +++ b/src/firmware/main.c @@ -32,6 +32,7 @@ const char* Notice = "Copyright (C) 2016 Michael McMaster "; +uint32_t lastSDPoll; void mainEarlyInit() { @@ -51,10 +52,11 @@ void mainInit() scsiDiskInit(); sdInit(); s2s_configInit(&scsiDev.boardCfg); + scsiPhyConfig(); + scsiInit(); s2s_debugInit(); - scsiInit(); MX_USB_DEVICE_Init(); // USB lun config now available. @@ -72,10 +74,7 @@ void mainInit() ++delaySeconds; } -#if 0 - uint32_t lastSDPoll = getTime_ms(); - sdCheckPresent(); -#endif + lastSDPoll = s2s_getTime_ms(); } void mainLoop() @@ -89,16 +88,28 @@ void mainLoop() #if 0 sdPoll(); +#endif if (unlikely(scsiDev.phase == BUS_FREE)) { - if (unlikely(elapsedTime_ms(lastSDPoll) > 200)) + if (unlikely(s2s_elapsedTime_ms(lastSDPoll) > 200)) { - lastSDPoll = getTime_ms(); - sdCheckPresent(); + lastSDPoll = s2s_getTime_ms(); + if (sdInit()) + { + s2s_configInit(&scsiDev.boardCfg); + scsiPhyConfig(); + scsiInit(); + + + USBD_Stop(&hUsbDeviceFS); + s2s_delay_ms(128); + USBD_Start(&hUsbDeviceFS); + } } else { +#if 0 // Wait for our 1ms timer to save some power. // There's an interrupt on the SEL signal to ensure we respond // quickly to any SCSI commands. The selection abort time is @@ -110,13 +121,13 @@ void mainLoop() __WFI(); // Will wake on interrupt, regardless of mask } CyExitCriticalSection(interruptState); +#endif } } else if (scsiDev.phase >= 0) { // don't waste time scanning SD cards while we're doing disk IO - lastSDPoll = getTime_ms(); + lastSDPoll = s2s_getTime_ms(); } -#endif } diff --git a/src/firmware/scsi.c b/src/firmware/scsi.c index 297a1bd8..c4b53a57 100755 --- a/src/firmware/scsi.c +++ b/src/firmware/scsi.c @@ -70,6 +70,7 @@ static void enter_BusFree() // Bus settle delay + bus clear delay = 1200ns s2s_delay_us(2); + s2s_ledOff(); scsiDev.phase = BUS_FREE; } @@ -459,7 +460,6 @@ static void scsiReset() s2s_ledOff(); scsiPhyReset(); - // TODO SCSI_Out_Ctl_Write(0); scsiDev.parityError = 0; scsiDev.phase = BUS_FREE; @@ -896,6 +896,8 @@ void scsiPoll(void) void scsiInit() { + static int firstInit = 1; + scsiDev.atnFlag = 0; scsiDev.resetFlag = 1; scsiDev.phase = BUS_FREE; @@ -920,10 +922,18 @@ void scsiInit() } scsiDev.targets[i].reservedId = -1; scsiDev.targets[i].reserverId = -1; - scsiDev.targets[i].unitAttention = POWER_ON_RESET; + if (firstInit) + { + scsiDev.targets[i].unitAttention = POWER_ON_RESET; + } + else + { + scsiDev.targets[i].unitAttention = PARAMETERS_CHANGED; + } scsiDev.targets[i].sense.code = NO_SENSE; scsiDev.targets[i].sense.asc = NO_ADDITIONAL_SENSE_INFORMATION; } + firstInit = 0; } /* TODO REENABLE diff --git a/src/firmware/scsiPhy.c b/src/firmware/scsiPhy.c index bdaef589..1a8a6aea 100755 --- a/src/firmware/scsiPhy.c +++ b/src/firmware/scsiPhy.c @@ -26,6 +26,8 @@ #include "fpga.h" #include "led.h" +#include + // Private DMA variables. static int dmaInProgress = 0; @@ -133,7 +135,7 @@ scsiReadDMA(uint8_t* data, uint32_t count) scsiTxDMAComplete = 1; // TODO not used much scsiRxDMAComplete = 0; // TODO not used much - HAL_DMA_Start(&fsmcToMem, (uint32_t) SCSI_FIFO_DATA, (uint32_t) data, count); // TODO MM count/4 for tx + HAL_DMA_Start(&fsmcToMem, (uint32_t) SCSI_FIFO_DATA, (uint32_t) data, count); } int @@ -424,6 +426,61 @@ void scsiPhyReset() } #endif + // FPGA comms test code + #ifdef FPGA_TEST + + while(1) + { + for (int j = 0; j < SCSI_FIFO_DEPTH; ++j) + { + scsiDev.data[j] = j; + } + + *SCSI_CTRL_PHASE = DATA_IN; + HAL_DMA_Start( + &memToFSMC, + (uint32_t) &scsiDev.data[0], + (uint32_t) SCSI_FIFO_DATA, + SCSI_FIFO_DEPTH / 4); + + HAL_DMA_PollForTransfer( + &memToFSMC, + HAL_DMA_FULL_TRANSFER, + 0xffffffff); + + memset(&scsiDev.data[0], 0, SCSI_FIFO_DEPTH); + + *SCSI_CTRL_PHASE = DATA_OUT; + HAL_DMA_Start( + &fsmcToMem, + (uint32_t) SCSI_FIFO_DATA, + (uint32_t) &scsiDev.data[0], + SCSI_FIFO_DEPTH); + + HAL_DMA_PollForTransfer( + &fsmcToMem, + HAL_DMA_FULL_TRANSFER, + 0xffffffff); + + for (int j = 0; j < SCSI_FIFO_DEPTH; ++j) + { + if (scsiDev.data[j] != (uint8_t) j) + { + while (1) + { + s2s_ledOn(); + s2s_delay_ms(100); + s2s_ledOff(); + s2s_delay_ms(100); + } + } + } + + s2s_fpgaReset(); + + } + #endif + } static void scsiPhyInitDMA() diff --git a/src/firmware/scsiPhy.h b/src/firmware/scsiPhy.h index dff2f345..6c72e415 100755 --- a/src/firmware/scsiPhy.h +++ b/src/firmware/scsiPhy.h @@ -30,13 +30,14 @@ #define SCSI_STS_FIFO_COMPLETE ((volatile uint8_t*)0x60000012) #define SCSI_STS_SELECTED ((volatile uint8_t*)0x60000013) #define SCSI_STS_SCSI ((volatile uint8_t*)0x60000014) +#define SCSI_STS_DBX ((volatile uint8_t*)0x60000015) #define SCSI_FIFO_DATA ((volatile uint8_t*)0x60000020) #define SCSI_FIFO_DEPTH 512 -#define scsiPhyFifoFull() ((*SCSI_STS_FIFO & 0x02) == 0x02) -#define scsiPhyFifoEmpty() ((*SCSI_STS_FIFO & 0x01) == 0x01) +#define scsiPhyFifoFull() ((*SCSI_STS_FIFO & 0x01) == 0x01) +#define scsiPhyFifoEmpty() ((*SCSI_STS_FIFO & 0x02) == 0x02) #define scsiPhyFifoFlip() \ {\ @@ -44,9 +45,6 @@ *SCSI_FIFO_SEL = scsiPhyFifoSel; \ } -// Clear 4 byte fifo -#define scsiPhyFifoClear() (void) scsiPhyRx(); (void) scsiPhyRx(); (void) scsiPhyRx(); (void) scsiPhyRx(); - #define scsiPhyTx(val) *SCSI_FIFO_DATA = (val) #define scsiPhyRx() *SCSI_FIFO_DATA #define scsiPhyComplete() ((*SCSI_STS_FIFO_COMPLETE & 0x01) == 0x01) diff --git a/src/firmware/sd.c b/src/firmware/sd.c index 08d6a195..8f0d5034 100755 --- a/src/firmware/sd.c +++ b/src/firmware/sd.c @@ -787,35 +787,12 @@ static void sdInitDMA() { init = 1; - //TODO MM SEE STUPID SD_DMA_RxCplt that require the SD IRQs to preempt -// Configured with 4 bits preemption, NO sub priority. - HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 8, 0); - HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); - HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 8, 0); - HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); -#if 0 - sdDMATxChan = - SD_TX_DMA_DmaInitialize( - 2, // Bytes per burst - 1, // request per burst - HI16(CYDEV_SRAM_BASE), - HI16(CYDEV_PERIPH_BASE) - ); - - sdDMARxChan = - SD_RX_DMA_DmaInitialize( - 1, // Bytes per burst - 1, // request per burst - HI16(CYDEV_PERIPH_BASE), - HI16(CYDEV_SRAM_BASE) - ); - - CyDmaChDisable(sdDMATxChan); - CyDmaChDisable(sdDMARxChan); - - SD_RX_DMA_COMPLETE_StartEx(sdRxISR); - SD_TX_DMA_COMPLETE_StartEx(sdTxISR); -#endif + //TODO MM SEE STUPID SD_DMA_RxCplt that require the SD IRQs to preempt + // Configured with 4 bits preemption, NO sub priority. + HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 8, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); + HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 8, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); } } @@ -829,97 +806,24 @@ void sdTmpWrite(uint8_t* data, uint32_t lba, int sectors) BSP_SD_WriteBlocks_DMA((uint32_t*) data, lba * 512ll, 512, sectors); } -int sdInit() +static void sdClear() { - int result = 0; - //int i; - - // TODO sdCmdState = CMD_STATE_IDLE; sdDev.version = 0; sdDev.ccs = 0; sdDev.capacity = 0; memset(sdDev.csd, 0, sizeof(sdDev.csd)); memset(sdDev.cid, 0, sizeof(sdDev.cid)); +} -// TODO should be in POLL method! - - sdInitDMA(); -#if 0 - - SD_CS_SetDriveMode(SD_CS_DM_STRONG); - SD_CS_Write(1); // Set CS inactive (active low) - - // Set the SPI clock for 400kHz transfers - // 25MHz / 400kHz approx factor of 63. - // The register contains (divider - 1) - uint16_t clkDiv25MHz = SD_Data_Clk_GetDividerRegister(); - SD_Data_Clk_SetDivider(((clkDiv25MHz + 1) * 63) - 1); - // Wait for the clock to settle. - CyDelayUs(1); - - SDCard_Start(); // Enable SPI hardware - - // Power on sequence. 74 clock cycles of a "1" while CS unasserted. - for (i = 0; i < 10; ++i) - { - sdSpiByte(0xFF); - } - - SD_CS_Write(0); // Set CS active (active low) - CyDelayUs(1); - - sdSpiByte(0xFF); - v = sdDoCommand(SD_GO_IDLE_STATE, 0, 1, 0); - if(v != 1){goto bad;} - - ledOn(); - if (!sendIfCond()) goto bad; // Sets V1 or V2 flag CMD8 - if (!sdOpCond()) goto bad; // ACMD41. Wait for init completes. - if (!sdReadOCR()) goto bad; // CMD58. Get CCS flag. Only valid after init. - - // This command will be ignored if sdDev.ccs is set. - // SDHC and SDXC are always 512bytes. - v = sdCRCCommandAndResponse(SD_SET_BLOCKLEN, SD_SECTOR_SIZE); //Force sector size - if(v){goto bad;} - v = sdCRCCommandAndResponse(SD_CRC_ON_OFF, 0); //crc off - if(v){goto bad;} - - // now set the sd card back to full speed. - // The SD Card spec says we can run SPI @ 25MHz - SDCard_Stop(); - - // We can't run at full-speed with the pullup resistors enabled. - SD_MISO_SetDriveMode(SD_MISO_DM_DIG_HIZ); - SD_MOSI_SetDriveMode(SD_MOSI_DM_STRONG); - SD_SCK_SetDriveMode(SD_SCK_DM_STRONG); - - SD_Data_Clk_SetDivider(clkDiv25MHz); - CyDelayUs(1); - SDCard_Start(); - - // Clear out rubbish data through clock change - CyDelayUs(1); - SDCard_ReadRxStatus(); - SDCard_ReadTxStatus(); - SDCard_ClearFIFO(); - - if (!sdReadCSD()) goto bad; - sdReadCID(); - - result = 1; - goto out; +static int sdDoInit() +{ + int result = 0; -bad: - SD_Data_Clk_SetDivider(clkDiv25MHz); // Restore the clock for our next retry - sdDev.capacity = 0; + // TODO sdCmdState = CMD_STATE_IDLE; + sdClear(); -out: - sdClearStatus(); - ledOff(); - return result; -#endif - uint8_t error = BSP_SD_Init(); + int8_t error = BSP_SD_Init(); if (error == MSD_OK) { memcpy(sdDev.csd, &SDCardInfo.SD_csd, sizeof(sdDev.csd)); @@ -1019,37 +923,54 @@ void sdPoll() } } -void sdCheckPresent() +#endif +int sdInit() { // Check if there's an SD card present. + int result = 0; + +#if 0 if ((scsiDev.phase == BUS_FREE) && (sdIOState == SD_IDLE) && (sdCmdState == CMD_STATE_IDLE)) +#endif + static int firstInit = 1; + + if (firstInit) { - // The CS line is pulled high by the SD card. - // De-assert the line, and check if it's high. - // This isn't foolproof as it'll be left floating without - // an SD card. We can't use the built-in pull-down resistor as it will - // overpower the SD pullup resistor. - SD_CS_Write(0); - SD_CS_SetDriveMode(SD_CS_DM_DIG_HIZ); - - // Delay extended to work with 60cm cables running cards at 2.85V - CyDelayCycles(128); - uint8_t cs = SD_CS_Read(); - SD_CS_SetDriveMode(SD_CS_DM_STRONG) ; + blockDev.state &= ~(DISK_PRESENT | DISK_INITIALISED); + sdClear(); + sdInitDMA(); + } + + if (scsiDev.phase == BUS_FREE) + { + uint8_t cs = HAL_GPIO_ReadPin(nSD_CD_GPIO_Port, nSD_CD_Pin) ? 0 : 1; + uint8_t wp = HAL_GPIO_ReadPin(nSD_WP_GPIO_Port, nSD_WP_Pin) ? 0 : 1; if (cs && !(blockDev.state & DISK_PRESENT)) { - static int firstInit = 1; + s2s_ledOn(); // Debounce - CyDelay(250); + if (!firstInit) + { + s2s_delay_ms(250); + } - if (sdInit()) + if (sdDoInit()) { blockDev.state |= DISK_PRESENT | DISK_INITIALISED; + if (wp) + { + blockDev.state |= DISK_WP; + } + else + { + blockDev.state &= ~DISK_WP; + } + // Always "start" the device. Many systems (eg. Apple System 7) // won't respond properly to // LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED sense @@ -1057,15 +978,21 @@ void sdCheckPresent() // START STOP UNIT command. blockDev.state |= DISK_STARTED; - if (!firstInit) + result = 1; + + s2s_ledOff(); + } + else + { + for (int i = 0; i < 10; ++i) { - int i; - for (i = 0; i < MAX_SCSI_TARGETS; ++i) - { - scsiDev.targets[i].unitAttention = PARAMETERS_CHANGED; - } + // visual indicator of SD error + s2s_ledOff(); + s2s_delay_ms(50); + s2s_ledOn(); + s2s_delay_ms(50); } - firstInit = 0; + s2s_ledOff(); } } else if (!cs && (blockDev.state & DISK_PRESENT)) @@ -1074,12 +1001,16 @@ void sdCheckPresent() blockDev.state &= ~DISK_PRESENT; blockDev.state &= ~DISK_INITIALISED; int i; - for (i = 0; i < MAX_SCSI_TARGETS; ++i) + for (i = 0; i < S2S_MAX_TARGETS; ++i) { scsiDev.targets[i].unitAttention = PARAMETERS_CHANGED; } + + HAL_SD_DeInit(&hsd); } } + firstInit = 0; + + return result; } -#endif diff --git a/src/firmware/sd.h b/src/firmware/sd.h index 878d9f06..bb8c0574 100755 --- a/src/firmware/sd.h +++ b/src/firmware/sd.h @@ -82,7 +82,6 @@ void sdReadSingleSectorDMA(uint32_t lba, uint8_t* outputBuffer); int sdReadSectorDMAPoll(); void sdCompleteTransfer(void); -void sdCheckPresent(); void sdPoll(); #endif diff --git a/src/firmware/usb_device/usbd_msc_storage_sd.c b/src/firmware/usb_device/usbd_msc_storage_sd.c index db6e411b..3cb44873 100755 --- a/src/firmware/usb_device/usbd_msc_storage_sd.c +++ b/src/firmware/usb_device/usbd_msc_storage_sd.c @@ -20,6 +20,7 @@ #include "stm32f2xx.h" #include "bsp_driver_sd.h" #include "../bsp.h" +#include "../disk.h" #include "../led.h" #include "../sd.h" #include "../config.h" @@ -114,15 +115,20 @@ uint32_t s2s_usbd_storage_Inquiry (uint8_t lun, uint8_t* buf, uint8_t maxlen) return s2s_getStandardInquiry(cfg, buf, maxlen); } -int8_t s2s_usbd_storage_IsReady (uint8_t lun) +int8_t s2s_usbd_storage_IsReady (uint8_t lun) { - return (0); + const S2S_TargetCfg* cfg = getUsbConfig(lun); + return ( + cfg && + (blockDev.state & DISK_PRESENT) && + (blockDev.state & DISK_INITIALISED) + ) ? 0 : 1; // inverse logic } int8_t s2s_usbd_storage_IsWriteProtected (uint8_t lun) { - return 0; + return blockDev.state & DISK_WP; } int8_t s2s_usbd_storage_Read (uint8_t lun, -- 2.38.5