From 9c274240d87cb4a5436bef907353959595cbd0be Mon Sep 17 00:00:00 2001 From: Dominic Clifton Date: Wed, 12 Jun 2019 15:19:14 +0200 Subject: [PATCH 1/2] Allow SDCARD to be used for config/eeprom storage. * On boot SPI or SDIO is initialised. * Filesystem is initialised (including creation of blackbox freespace file) * Empty config file is created if it doesn't exist, or read if it does. * If config is invalid/empty then config file is written to, then read back and verified. Enable as follows. target.h: target.c: uint8_t eepromData[EEPROM_SIZE]; Changes: - Replace boolean init flags with single initFlags variables. - Avoid unused variable warnings. --- src/main/cli/cli.c | 1 + src/main/config/config_eeprom.c | 152 ++++++++++++++++++++++++++++-- src/main/config/config_streamer.c | 26 ++--- src/main/drivers/system.h | 11 ++- src/main/fc/init.c | 102 +++++++++++++++++--- src/main/target/common_post.h | 2 +- 6 files changed, 259 insertions(+), 35 deletions(-) diff --git a/src/main/cli/cli.c b/src/main/cli/cli.c index 6b143f147f..f4e545cd4e 100644 --- a/src/main/cli/cli.c +++ b/src/main/cli/cli.c @@ -3315,6 +3315,7 @@ static void cliRebootEx(bool bootLoader) bufWriterFlush(cliWriter); waitForSerialPortToFinishTransmitting(cliPort); stopPwmAllMotors(); + if (bootLoader) { systemResetToBootloader(); return; diff --git a/src/main/config/config_eeprom.c b/src/main/config/config_eeprom.c index 4965c0a9d2..32d13d498d 100644 --- a/src/main/config/config_eeprom.c +++ b/src/main/config/config_eeprom.c @@ -34,6 +34,10 @@ #include "pg/pg.h" #include "fc/config.h" +#ifdef EEPROM_IN_SDCARD +#include "io/asyncfatfs/asyncfatfs.h" +#endif + #include "drivers/flash.h" #include "drivers/system.h" @@ -79,7 +83,7 @@ typedef struct { uint32_t word; } PG_PACKED packingTest_t; -#ifdef EEPROM_IN_EXTERNAL_FLASH +#if defined(EEPROM_IN_EXTERNAL_FLASH) bool loadEEPROMFromExternalFlash(void) { const flashPartition_t *flashPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG); @@ -102,6 +106,134 @@ bool loadEEPROMFromExternalFlash(void) return success; } +#elif defined(EEPROM_IN_SDCARD) + +enum { + FILE_STATE_NONE = 0, + FILE_STATE_BUSY = 1, + FILE_STATE_FAILED, + FILE_STATE_COMPLETE, +}; + +uint8_t fileState = FILE_STATE_NONE; + +const char *defaultSDCardConfigFilename = "CONFIG.BIN"; + +void saveEEPROMToSDCardCloseContinue(void) +{ + if (fileState != FILE_STATE_FAILED) { + fileState = FILE_STATE_COMPLETE; + } +} + +void saveEEPROMToSDCardWriteContinue(afatfsFilePtr_t file) +{ + if (!file) { + fileState = FILE_STATE_FAILED; + return; + } + + uint32_t totalBytesWritten = 0; + uint32_t bytesWritten = 0; + bool success; + + do { + bytesWritten = afatfs_fwrite(file, &eepromData[totalBytesWritten], EEPROM_SIZE - totalBytesWritten); + totalBytesWritten += bytesWritten; + success = (totalBytesWritten == EEPROM_SIZE); + + afatfs_poll(); + } while (!success && afatfs_getLastError() == AFATFS_ERROR_NONE); + + if (!success) { + fileState = FILE_STATE_FAILED; + } + + while (!afatfs_fclose(file, saveEEPROMToSDCardCloseContinue)) { + afatfs_poll(); + } +} + +bool saveEEPROMToSDCard(void) +{ + fileState = FILE_STATE_BUSY; + bool result = afatfs_fopen(defaultSDCardConfigFilename, "w+", saveEEPROMToSDCardWriteContinue); + if (!result) { + return false; + } + + while (fileState == FILE_STATE_BUSY) { + afatfs_poll(); + } + + while (!afatfs_flush()) { + afatfs_poll(); + }; + + return (fileState == FILE_STATE_COMPLETE); +} + +void loadEEPROMFromSDCardCloseContinue(void) +{ + if (fileState != FILE_STATE_FAILED) { + fileState = FILE_STATE_COMPLETE; + } +} + +void loadEEPROMFromSDCardReadContinue(afatfsFilePtr_t file) +{ + if (!file) { + fileState = FILE_STATE_FAILED; + return; + } + + fileState = FILE_STATE_BUSY; + + uint32_t totalBytesRead = 0; + uint32_t bytesRead = 0; + bool success; + + if (afatfs_feof(file)) { + // empty file, nothing to load. + memset(eepromData, 0x00, EEPROM_SIZE); + success = true; + } else { + + do { + bytesRead = afatfs_fread(file, &eepromData[totalBytesRead], EEPROM_SIZE - totalBytesRead); + totalBytesRead += bytesRead; + success = (totalBytesRead == EEPROM_SIZE); + + afatfs_poll(); + } while (!success && afatfs_getLastError() == AFATFS_ERROR_NONE); + } + + if (!success) { + fileState = FILE_STATE_FAILED; + } + + while (!afatfs_fclose(file, loadEEPROMFromSDCardCloseContinue)) { + afatfs_poll(); + } + + return; +} + +bool loadEEPROMFromSDCard(void) +{ + fileState = FILE_STATE_BUSY; + // use "w+" mode here to ensure the file is created now - in w+ mode we can read and write and the seek position is 0 on existing files, ready for reading. + bool result = afatfs_fopen(defaultSDCardConfigFilename, "w+", loadEEPROMFromSDCardReadContinue); + if (!result) { + return false; + } + + while (fileState == FILE_STATE_BUSY) { + afatfs_poll(); + } + + return (fileState == FILE_STATE_COMPLETE); +} #endif #ifdef EEPROM_IN_FILE @@ -120,16 +252,20 @@ void initEEPROM(void) STATIC_ASSERT(sizeof(configFooter_t) == 2, footer_size_failed); STATIC_ASSERT(sizeof(configRecord_t) == 6, record_size_failed); -#ifdef EEPROM_IN_FILE +#if defined(EEPROM_IN_FILE) loadEEPROMFromFile(); -#endif - -#ifdef EEPROM_IN_EXTERNAL_FLASH +#elif defined(EEPROM_IN_EXTERNAL_FLASH) bool eepromLoaded = loadEEPROMFromExternalFlash(); if (!eepromLoaded) { // Flash read failed - just die now failureMode(FAILURE_FLASH_READ_FAILED); } +#elif defined(EEPROM_IN_SDCARD) + bool eepromLoaded = loadEEPROMFromSDCard(); + if (!eepromLoaded) { + // SDCard read failed - just die now + failureMode(FAILURE_SDCARD_READ_FAILED); + } #endif } @@ -317,6 +453,10 @@ void writeConfigToEEPROM(void) #ifdef EEPROM_IN_EXTERNAL_FLASH // copy it back from flash to the in-memory buffer. success = loadEEPROMFromExternalFlash(); +#endif +#ifdef EEPROM_IN_SDCARD + // copy it back from flash to the in-memory buffer. + success = loadEEPROMFromSDCard(); #endif } } @@ -327,5 +467,5 @@ void writeConfigToEEPROM(void) } // Flash write failed - just die now - failureMode(FAILURE_FLASH_WRITE_FAILED); + failureMode(FAILURE_CONFIG_STORE_FAILURE); } diff --git a/src/main/config/config_streamer.c b/src/main/config/config_streamer.c index 341cab0d6a..0ad3dc8a63 100644 --- a/src/main/config/config_streamer.c +++ b/src/main/config/config_streamer.c @@ -27,7 +27,7 @@ #include "config/config_streamer.h" -#if defined(STM32H750xx) && !(defined(EEPROM_IN_EXTERNAL_FLASH) || defined(EEPROM_IN_RAM)) +#if defined(STM32H750xx) && !(defined(EEPROM_IN_EXTERNAL_FLASH) || defined(EEPROM_IN_RAM) || defined(EEPROM_IN_SDCARD)) #error "STM32750xx only has one flash page which contains the bootloader, no spare flash pages available, use external storage for persistent config or ram for target testing" #endif // @todo this is not strictly correct for F4/F7, where sector sizes are variable @@ -82,7 +82,7 @@ void config_streamer_start(config_streamer_t *c, uintptr_t base, int size) c->address = base; c->size = size; if (!c->unlocked) { -#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_EXTERNAL_FLASH) +#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_EXTERNAL_FLASH) || defined(EEPROM_IN_SDCARD) // NOP #elif defined(EEPROM_IN_FLASH) || defined(EEPROM_IN_FILE) #if defined(STM32F7) || defined(STM32H7) @@ -116,7 +116,7 @@ void config_streamer_start(config_streamer_t *c, uintptr_t base, int size) c->err = 0; } -#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_EXTERNAL_FLASH) +#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_EXTERNAL_FLASH) || defined(EEPROM_IN_SDCARD) // No flash sector method required. #elif defined(EEPROM_IN_FLASH) #if defined(STM32F745xx) || defined(STM32F746xx) || defined(STM32F765xx) @@ -168,7 +168,7 @@ static uint32_t getFLASHSectorForEEPROM(void) // Not good while (1) { - failureMode(FAILURE_FLASH_WRITE_FAILED); + failureMode(FAILURE_CONFIG_STORE_FAILURE); } } @@ -205,7 +205,7 @@ static uint32_t getFLASHSectorForEEPROM(void) // Not good while (1) { - failureMode(FAILURE_FLASH_WRITE_FAILED); + failureMode(FAILURE_CONFIG_STORE_FAILURE); } } @@ -254,7 +254,7 @@ static uint32_t getFLASHSectorForEEPROM(void) // Not good while (1) { - failureMode(FAILURE_FLASH_WRITE_FAILED); + failureMode(FAILURE_CONFIG_STORE_FAILURE); } } @@ -296,7 +296,7 @@ static void getFLASHSectorForEEPROM(uint32_t *bank, uint32_t *sector) } else { // Not good while (1) { - failureMode(FAILURE_FLASH_WRITE_FAILED); + failureMode(FAILURE_CONFIG_STORE_FAILURE); } } @@ -338,7 +338,7 @@ static void getFLASHSectorForEEPROM(uint32_t *bank, uint32_t *sector) } else { // Not good while (1) { - failureMode(FAILURE_FLASH_WRITE_FAILED); + failureMode(FAILURE_CONFIG_STORE_FAILURE); } } } @@ -386,7 +386,7 @@ static int write_word(config_streamer_t *c, config_streamer_buffer_align_type_t flashPageProgramContinue((uint8_t *)buffer, CONFIG_STREAMER_BUFFER_SIZE); -#elif defined(EEPROM_IN_RAM) +#elif defined(EEPROM_IN_RAM) || defined(EEPROM_IN_SDCARD) if (c->address == (uintptr_t)&eepromData[0]) { memset(eepromData, 0, sizeof(eepromData)); } @@ -394,7 +394,7 @@ static int write_word(config_streamer_t *c, config_streamer_buffer_align_type_t uint64_t *dest_addr = (uint64_t *)c->address; uint64_t *src_addr = (uint64_t*)buffer; uint8_t row_index = 4; - /* Program the 256 bits flash word */ + /* copy the 256 bits flash word */ do { *dest_addr++ = *src_addr++; @@ -509,7 +509,11 @@ int config_streamer_flush(config_streamer_t *c) int config_streamer_finish(config_streamer_t *c) { if (c->unlocked) { -#if defined(EEPROM_IN_EXTERNAL_FLASH) +#if defined(EEPROM_IN_SDCARD) + bool saveEEPROMToSDCard(void); // XXX forward declaration to avoid circular dependency between config_streamer / config_eeprom + saveEEPROMToSDCard(); + // TODO overwrite the data in the file on the SD card. +#elif defined(EEPROM_IN_EXTERNAL_FLASH) flashFlush(); #elif defined(EEPROM_IN_RAM) // NOP diff --git a/src/main/drivers/system.h b/src/main/drivers/system.h index 9d5544a299..4aa17bec99 100644 --- a/src/main/drivers/system.h +++ b/src/main/drivers/system.h @@ -31,10 +31,17 @@ typedef enum { FAILURE_ACC_INIT, FAILURE_ACC_INCOMPATIBLE, FAILURE_INVALID_EEPROM_CONTENTS, - FAILURE_FLASH_WRITE_FAILED, + FAILURE_CONFIG_STORE_FAILURE, FAILURE_GYRO_INIT_FAILED, FAILURE_FLASH_READ_FAILED, - FAILURE_EXTERNAL_FLASH_INIT_FAILED + FAILURE_FLASH_WRITE_FAILED, + FAILURE_FLASH_INIT_FAILED, // RESERVED + FAILURE_EXTERNAL_FLASH_READ_FAILED, // RESERVED + FAILURE_EXTERNAL_FLASH_WRITE_FAILED, // RESERVED + FAILURE_EXTERNAL_FLASH_INIT_FAILED, + FAILURE_SDCARD_READ_FAILED, + FAILURE_SDCARD_WRITE_FAILED, + FAILURE_SDCARD_INITIALISATION_FAILED, } failureMode_e; #define WARNING_FLASH_DURATION_MS 50 diff --git a/src/main/fc/init.c b/src/main/fc/init.c index 6c713c2511..0a5941f868 100644 --- a/src/main/fc/init.c +++ b/src/main/fc/init.c @@ -179,6 +179,8 @@ serialPort_t *loopbackPort; uint8_t systemState = SYSTEM_STATE_INITIALISING; +void SDIO_GPIO_Init(void); + void processLoopback(void) { #ifdef SOFTSERIAL_LOOPBACK @@ -246,6 +248,13 @@ static void configureSPIAndQuadSPI(void) #endif // USE_QUAD_SPI } +void sdCardAndFSInit() +{ + sdcardInsertionDetectInit(); + sdcard_init(sdcardConfig()); + afatfs_init(); +} + void init(void) { @@ -292,14 +301,69 @@ void init(void) } #endif -#ifdef EEPROM_IN_EXTERNAL_FLASH + enum { + FLASH_INIT_ATTEMPTED = (1 << 0), + SD_INIT_ATTEMPTED = (1 << 1), + SPI_AND_QSPI_INIT_ATTEMPTED = (1 << 2), + }; + uint8_t initFlags = 0; + +#ifdef EEPROM_IN_SDCARD + + // + // Config in sdcard presents an issue with pin configuration since the pin and sdcard configs for the + // sdcard are in the config which is on the sdcard which we can't read yet! + // + // FIXME We need to add configuration somewhere, e.g. bootloader image or reserved flash area, that can be read by the firmware. + // it's currently possible for the firmware resource allocation to be wrong after the config is loaded if the user changes the settings. + // This would cause undefined behaviour once the config is loaded. so for now, users must NOT change sdio/spi configs needed for + // the system to boot and/or to save the config. + // + // note that target specific SDCARD/SDIO/SPI/QUADSPI configs are + // also not supported in USE_TARGET_CONFIG/targetConfigure() when using EEPROM_IN_SDCARD. + // + + // + // IMPORTANT: all default flash and pin configurations must be valid for the target after pgResetAll() is called. + // Target designers must ensure other devices connected the same SPI/QUADSPI interface as the flash chip do not + // cause communication issues with the flash chip. e.g. use external pullups on SPI/QUADSPI CS lines. + // + +#ifdef TARGET_BUS_INIT +#error "EEPROM_IN_SDCARD and TARGET_BUS_INIT are mutually exclusive" +#endif + + pgResetAll(); + +#if defined(STM32H7) && defined(USE_SDCARD_SDIO) // H7 only for now, likely should be applied to F4/F7 too + SDIO_GPIO_Init(); +#endif +#ifdef USE_SDCARD_SPI + configureSPIAndQuadSPI(); + initFlags |= SPI_AND_QSPI_INIT_ATTEMPTED; +#endif + + sdCardAndFSInit(); + initFlags |= SD_INIT_ATTEMPTED; + + while (afatfs_getFilesystemState() != AFATFS_FILESYSTEM_STATE_READY) { + afatfs_poll(); + + if (afatfs_getFilesystemState() == AFATFS_FILESYSTEM_STATE_FATAL) { + failureMode(FAILURE_SDCARD_INITIALISATION_FAILED); + } + } + +#endif // EEPROM_IN_SDCARD + +#ifdef EEPROM_IN_EXTERNAL_FLASH // // Config on external flash presents an issue with pin configuration since the pin and flash configs for the // external flash are in the config which is on a chip which we can't read yet! // - // FIXME We need to add configuration into the bootloader image that can be read by the firmware. - // it's currently possible firmware and bootloader to become mismatched if the user changes them. + // FIXME We need to add configuration somewhere, e.g. bootloader image or reserved flash area, that can be read by the firmware. + // it's currently possible for the firmware resource allocation to be wrong after the config is loaded if the user changes the settings. // This would cause undefined behaviour once the config is loaded. so for now, users must NOT change flash/pin configs needed for // the system to boot and/or to save the config. // @@ -319,6 +383,8 @@ void init(void) #endif configureSPIAndQuadSPI(); + initFlags |= SPI_AND_QSPI_INIT_ATTEMPTED; + #ifndef USE_FLASH_CHIP #error "EEPROM_IN_EXTERNAL_FLASH requires USE_FLASH_CHIP to be defined." @@ -329,10 +395,10 @@ void init(void) if (!haveFlash) { failureMode(FAILURE_EXTERNAL_FLASH_INIT_FAILED); } + initFlags |= FLASH_INIT_ATTEMPTED; #endif // EEPROM_IN_EXTERNAL_FLASH - initEEPROM(); ensureEEPROMStructureIsValid(); @@ -517,9 +583,11 @@ void init(void) #else -#ifndef EEPROM_IN_EXTERNAL_FLASH - configureSPIAndQuadSPI(); -#endif // EEPROM_IN_EXTERNAL_FLASH + // Depending on compilation options SPI/QSPI initialisation may already be done. + if (!(initFlags & SPI_AND_QSPI_INIT_ATTEMPTED)) { + configureSPIAndQuadSPI(); + initFlags |= SPI_AND_QSPI_INIT_ATTEMPTED; + } #ifdef USE_USB_MSC /* MSC mode will start after init, but will not allow scheduler to run, @@ -567,10 +635,12 @@ void init(void) #endif #if defined(STM32H7) && defined(USE_SDCARD_SDIO) // H7 only for now, likely should be applied to F4/F7 too - void SDIO_GPIO_Init(void); - SDIO_GPIO_Init(); + if (!(initFlags & SD_INIT_ATTEMPTED)) { + SDIO_GPIO_Init(); + } #endif + #ifdef USE_VTX_RTC6705 bool useRTC6705 = rtc6705IOInit(vtxIOConfig()); #endif @@ -763,10 +833,11 @@ void init(void) } #endif -#ifndef EEPROM_IN_EXTERNAL_FLASH #ifdef USE_FLASH_CHIP - flashInit(flashConfig()); -#endif + if (!(initFlags & FLASH_INIT_ATTEMPTED)) { + flashInit(flashConfig()); + initFlags |= FLASH_INIT_ATTEMPTED; + } #endif #ifdef USE_FLASHFS flashfsInit(); @@ -776,9 +847,10 @@ void init(void) #ifdef USE_SDCARD if (blackboxConfig()->device == BLACKBOX_DEVICE_SDCARD) { if (sdcardConfig()->mode) { - sdcardInsertionDetectInit(); - sdcard_init(sdcardConfig()); - afatfs_init(); + if (!(initFlags & SD_INIT_ATTEMPTED)) { + initFlags |= SD_INIT_ATTEMPTED; + sdCardAndFSInit(); + } } else { blackboxConfigMutable()->device = BLACKBOX_DEVICE_NONE; } diff --git a/src/main/target/common_post.h b/src/main/target/common_post.h index 933443d49a..dd4823f373 100644 --- a/src/main/target/common_post.h +++ b/src/main/target/common_post.h @@ -338,7 +338,7 @@ #undef USE_ESCSERIAL #endif -#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_FILE) || defined(EEPROM_IN_EXTERNAL_FLASH) +#if defined(EEPROM_IN_RAM) || defined(EEPROM_IN_FILE) || defined(EEPROM_IN_EXTERNAL_FLASH) || defined(EEPROM_IN_SDCARD) #ifndef EEPROM_SIZE #define EEPROM_SIZE 4096 #endif From 58f43634bd001bc8c1b6c0ac087d3b385d0b35ed Mon Sep 17 00:00:00 2001 From: Dominic Clifton Date: Wed, 12 Jun 2019 14:39:11 +0200 Subject: [PATCH 2/2] Declare eepromData for all builds that require it, rather than having the target.c define it. --- src/main/config/config_streamer.c | 9 +++++++++ src/main/target/SITL/target.c | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/config/config_streamer.c b/src/main/config/config_streamer.c index 0ad3dc8a63..af7638379a 100644 --- a/src/main/config/config_streamer.c +++ b/src/main/config/config_streamer.c @@ -27,6 +27,15 @@ #include "config/config_streamer.h" +#if !defined(EEPROM_IN_FLASH) +#if defined(EEPROM_IN_RAM) && defined(PERSISTENT) +PERSISTENT uint8_t eepromData[EEPROM_SIZE]; +#else +uint8_t eepromData[EEPROM_SIZE]; +#endif +#endif + + #if defined(STM32H750xx) && !(defined(EEPROM_IN_EXTERNAL_FLASH) || defined(EEPROM_IN_RAM) || defined(EEPROM_IN_SDCARD)) #error "STM32750xx only has one flash page which contains the bootloader, no spare flash pages available, use external storage for persistent config or ram for target testing" #endif diff --git a/src/main/target/SITL/target.c b/src/main/target/SITL/target.c index 0930108a22..b353321213 100644 --- a/src/main/target/SITL/target.c +++ b/src/main/target/SITL/target.c @@ -468,7 +468,6 @@ char _Min_Stack_Size; // fake EEPROM static FILE *eepromFd = NULL; -uint8_t eepromData[EEPROM_SIZE]; void FLASH_Unlock(void) { if (eepromFd != NULL) {