From b8085b170e972645c17c907cd6265bcc26d41a3f Mon Sep 17 00:00:00 2001 From: Bruce Luckcuck Date: Sat, 1 Dec 2018 10:07:22 -0500 Subject: [PATCH] MSC on-board flash: Persist RTC across reboot and use as file timestamp Adds support to persist the RTC (if set) across the reboot if entering mass storage mode for on-board flash. The value is then used as the timestamp for the files exposed in the virtual FAT32 filesystem. The files will then have reasonable creation dates when copied to the host computer. If the RTC is not set (or supported), then the default timestamp of 2018-01-01 will be used (unchanged from previous). Included some improvements to the RTC functions and exposed the `tz_offsetMinutes` in the `timeConfig` PG. Support already existed for timezone offsets but the parameter wasn't exposed to the user and couldn't be set. Move timezone offset up a layer as a parameter to systemResetToMsc() Adds support for specifying a custom timezone offset from both the CLI and MSP calls to enter mass storage mode. Added an option timezone offset minutes to the CLI `msc` command. If no parameter is specified then the default as specified by `timezone_offset_minutes` will be used. So to reboot into mass storage mode and force the file timestamps to be in UTC, use `msc 0`. Added reboot message `MSP_REBOOT_MSC_UTC` to support rebooting into mass storage mode and forcing the timestamps to use UTC time (0 offset). The Configurator will need to be modified to use this message for operating systems that expect UTC times for FAT file systems (like Linux). --- src/main/common/time.c | 29 +++++++++++++++++++++++++++++ src/main/common/time.h | 5 +++++ src/main/drivers/persistent.h | 2 ++ src/main/drivers/usb_msc.h | 2 +- src/main/drivers/usb_msc_f4xx.c | 9 ++++++++- src/main/drivers/usb_msc_f7xx.c | 9 ++++++++- src/main/fc/init.c | 11 +++++++++++ src/main/interface/cli.c | 21 ++++++++++++++++++--- src/main/interface/msp.c | 11 +++++++++-- src/main/interface/settings.c | 6 ++++++ src/main/msc/emfat_file.c | 33 +++++++++++++++++++++++++++------ src/main/msc/usbd_storage.c | 3 +++ src/main/msc/usbd_storage.h | 2 ++ src/main/target/common_post.h | 4 ++++ src/main/target/common_pre.h | 2 ++ src/test/unit/osd_unittest.cc | 3 +++ 16 files changed, 138 insertions(+), 14 deletions(-) diff --git a/src/main/common/time.c b/src/main/common/time.c index 3541f237e8..c6044e178d 100644 --- a/src/main/common/time.c +++ b/src/main/common/time.c @@ -30,6 +30,8 @@ #include "common/printf.h" #include "common/time.h" +#include "drivers/persistent.h" + #include "pg/pg_ids.h" #include "drivers/time.h" @@ -262,4 +264,31 @@ bool rtcSetDateTime(dateTime_t *dt) return rtcSet(&t); } +void rtcPersistWrite(int16_t offsetMinutes) +{ + rtcTime_t workTime; + uint32_t highLongWord = 0; + uint32_t lowLongWord = 0; + if (rtcGet(&workTime)) { + workTime += (offsetMinutes * 60 * MILLIS_PER_SECOND); + highLongWord = (uint32_t)(workTime >> 32); + lowLongWord = (uint32_t)(workTime & 0xffffffff); + } + persistentObjectWrite(PERSISTENT_OBJECT_RTC_HIGH, highLongWord); + persistentObjectWrite(PERSISTENT_OBJECT_RTC_LOW, lowLongWord); +} + +bool rtcPersistRead(rtcTime_t *t) +{ + const uint32_t highLongWord = persistentObjectRead(PERSISTENT_OBJECT_RTC_HIGH); + const uint32_t lowLongWord = persistentObjectRead(PERSISTENT_OBJECT_RTC_LOW); + + if ((highLongWord != 0) || (lowLongWord != 0)) { + *t = ((uint64_t)highLongWord << 32) + lowLongWord; + return true; + } else { + return false; + } +} + #endif diff --git a/src/main/common/time.h b/src/main/common/time.h index b93860b56f..988422bba4 100644 --- a/src/main/common/time.h +++ b/src/main/common/time.h @@ -40,6 +40,9 @@ typedef uint32_t timeUs_t; #define TIMEUS_MAX UINT32_MAX #endif +#define TIMEZONE_OFFSET_MINUTES_MIN -780 // -13 hours +#define TIMEZONE_OFFSET_MINUTES_MAX 780 // +13 hours + static inline timeDelta_t cmpTimeUs(timeUs_t a, timeUs_t b) { return (timeDelta_t)(a - b); } #define FORMATTED_DATE_TIME_BUFSIZE 30 @@ -95,4 +98,6 @@ bool rtcSet(rtcTime_t *t); bool rtcGetDateTime(dateTime_t *dt); bool rtcSetDateTime(dateTime_t *dt); +void rtcPersistWrite(int16_t offsetMinutes); +bool rtcPersistRead(rtcTime_t *t); #endif diff --git a/src/main/drivers/persistent.h b/src/main/drivers/persistent.h index 90f0568d3f..aaf4f7535f 100644 --- a/src/main/drivers/persistent.h +++ b/src/main/drivers/persistent.h @@ -30,6 +30,8 @@ typedef enum { PERSISTENT_OBJECT_HSE_VALUE, PERSISTENT_OBJECT_OVERCLOCK_LEVEL, PERSISTENT_OBJECT_BOOTLOADER_REQUEST, + PERSISTENT_OBJECT_RTC_HIGH, // high 32 bits of rtcTime_t + PERSISTENT_OBJECT_RTC_LOW, // low 32 bits of rtcTime_t PERSISTENT_OBJECT_COUNT, } persistentObjectId_e; diff --git a/src/main/drivers/usb_msc.h b/src/main/drivers/usb_msc.h index d2467894a0..66f94e57e4 100644 --- a/src/main/drivers/usb_msc.h +++ b/src/main/drivers/usb_msc.h @@ -31,4 +31,4 @@ bool mscCheckBoot(void); uint8_t mscStart(void); bool mscCheckButton(void); void mscWaitForButton(void); -void systemResetToMsc(void); +void systemResetToMsc(int timezoneOffsetMinutes); diff --git a/src/main/drivers/usb_msc_f4xx.c b/src/main/drivers/usb_msc_f4xx.c index 52e2b6043a..de0257cfdc 100644 --- a/src/main/drivers/usb_msc_f4xx.c +++ b/src/main/drivers/usb_msc_f4xx.c @@ -159,11 +159,18 @@ void mscWaitForButton(void) } } -void systemResetToMsc(void) +void systemResetToMsc(int timezoneOffsetMinutes) { *((uint32_t *)0x2001FFF0) = MSC_MAGIC; __disable_irq(); + + // Persist the RTC across the reboot to use as the file timestamp +#ifdef USE_PERSISTENT_MSC_RTC + rtcPersistWrite(timezoneOffsetMinutes); +#else + UNUSED(timezoneOffsetMinutes); +#endif NVIC_SystemReset(); } #endif diff --git a/src/main/drivers/usb_msc_f7xx.c b/src/main/drivers/usb_msc_f7xx.c index 9e104f70f1..5f0029dc1c 100644 --- a/src/main/drivers/usb_msc_f7xx.c +++ b/src/main/drivers/usb_msc_f7xx.c @@ -164,11 +164,18 @@ void mscWaitForButton(void) } } -void systemResetToMsc(void) +void systemResetToMsc(int timezoneOffsetMinutes) { *((__IO uint32_t*) BKPSRAM_BASE + 16) = MSC_MAGIC; __disable_irq(); + + // Persist the RTC across the reboot to use as the file timestamp +#ifdef USE_PERSISTENT_MSC_RTC + rtcPersistWrite(timezoneOffsetMinutes); +#else + UNUSED(timezoneOffsetMinutes); +#endif NVIC_SystemReset(); } #endif diff --git a/src/main/fc/init.c b/src/main/fc/init.c index b03782c7be..016214ef9e 100644 --- a/src/main/fc/init.c +++ b/src/main/fc/init.c @@ -58,6 +58,7 @@ #include "drivers/light_led.h" #include "drivers/mco.h" #include "drivers/nvic.h" +#include "drivers/persistent.h" #include "drivers/pwm_esc_detect.h" #include "drivers/pwm_output.h" #include "drivers/rx/rx_pwm.h" @@ -89,6 +90,10 @@ #include "interface/cli.h" #include "interface/msp.h" +#ifdef USE_PERSISTENT_MSC_RTC +#include "msc/usbd_storage.h" +#endif + #include "msp/msp_serial.h" #include "pg/adc.h" @@ -428,6 +433,12 @@ void init(void) } #endif +#ifdef USE_PERSISTENT_MSC_RTC + // if we didn't enter MSC mode then clear the persistent RTC value + persistentObjectWrite(PERSISTENT_OBJECT_RTC_HIGH, 0); + persistentObjectWrite(PERSISTENT_OBJECT_RTC_LOW, 0); +#endif + #ifdef USE_I2C i2cHardwareConfigure(i2cConfig(0)); diff --git a/src/main/interface/cli.c b/src/main/interface/cli.c index 711bd6b8e9..aeb7c9b8cf 100644 --- a/src/main/interface/cli.c +++ b/src/main/interface/cli.c @@ -4483,16 +4483,27 @@ static void cliDiff(char *cmdline) #if defined(USE_USB_MSC) static void cliMsc(char *cmdline) { - UNUSED(cmdline); - if (mscCheckFilesystemReady()) { +#ifdef USE_RTC_TIME + int timezoneOffsetMinutes = timeConfig()->tz_offsetMinutes; + if (!isEmpty(cmdline)) { + timezoneOffsetMinutes = atoi(cmdline); + if ((timezoneOffsetMinutes < TIMEZONE_OFFSET_MINUTES_MIN) || (timezoneOffsetMinutes > TIMEZONE_OFFSET_MINUTES_MAX)) { + cliPrintErrorLinef("INVALID TIMEZONE OFFSET"); + return; + } + } +#else + int timezoneOffsetMinutes = 0; + UNUSED(cmdline); +#endif cliPrintHashLine("Restarting in mass storage mode"); cliPrint("\r\nRebooting"); bufWriterFlush(cliWriter); waitForSerialPortToFinishTransmitting(cliPort); stopPwmAllMotors(); - systemResetToMsc(); + systemResetToMsc(timezoneOffsetMinutes); } else { cliPrintHashLine("Storage not present or failed to initialize!"); } @@ -4598,7 +4609,11 @@ const clicmd_t cmdTable[] = { #endif CLI_COMMAND_DEF("motor", "get/set motor", " []", cliMotor), #ifdef USE_USB_MSC +#ifdef USE_RTC_TIME + CLI_COMMAND_DEF("msc", "switch into msc mode", "[]", cliMsc), +#else CLI_COMMAND_DEF("msc", "switch into msc mode", NULL, cliMsc), +#endif #endif CLI_COMMAND_DEF("name", "name of craft", NULL, cliName), #ifndef MINIMAL_CLI diff --git a/src/main/interface/msp.c b/src/main/interface/msp.c index 32dadcccea..55a900c754 100644 --- a/src/main/interface/msp.c +++ b/src/main/interface/msp.c @@ -138,6 +138,7 @@ enum { MSP_REBOOT_FIRMWARE = 0, MSP_REBOOT_BOOTLOADER, MSP_REBOOT_MSC, + MSP_REBOOT_MSC_UTC, MSP_REBOOT_COUNT, }; @@ -253,8 +254,14 @@ static void mspRebootFn(serialPort_t *serialPort) break; #if defined(USE_USB_MSC) case MSP_REBOOT_MSC: - systemResetToMsc(); - + case MSP_REBOOT_MSC_UTC: { +#ifdef USE_RTC_TIME + const int16_t timezoneOffsetMinutes = (rebootMode == MSP_REBOOT_MSC) ? timeConfig()->tz_offsetMinutes : 0; + systemResetToMsc(timezoneOffsetMinutes); +#else + systemResetToMsc(0); +#endif + } break; #endif default: diff --git a/src/main/interface/settings.c b/src/main/interface/settings.c index eff9fa81d9..73c7417b47 100644 --- a/src/main/interface/settings.c +++ b/src/main/interface/settings.c @@ -30,6 +30,7 @@ #include "cms/cms.h" #include "common/utils.h" +#include "common/time.h" #include "drivers/adc.h" #include "drivers/bus_i2c.h" @@ -1266,6 +1267,11 @@ const clivalue_t valueTable[] = { { "spektrum_spi_mfg_id", VAR_UINT8 | MASTER_VALUE | MODE_ARRAY, .config.array.length = 4, PG_RX_SPEKTRUM_SPI_CONFIG, offsetof(spektrumConfig_t, mfgId) }, { "spektrum_spi_num_channels", VAR_UINT8 | MASTER_VALUE, .config.minmax = { 0, DSM_MAX_CHANNEL_COUNT }, PG_RX_SPEKTRUM_SPI_CONFIG, offsetof(spektrumConfig_t, numChannels) }, #endif + +// PG_TIMECONFIG +#ifdef USE_RTC_TIME + { "timezone_offset_minutes", VAR_INT16 | MASTER_VALUE, .config.minmax = { TIMEZONE_OFFSET_MINUTES_MIN, TIMEZONE_OFFSET_MINUTES_MAX }, PG_TIME_CONFIG, offsetof(timeConfig_t, tz_offsetMinutes) }, +#endif }; const uint16_t valueTableEntryCount = ARRAYLEN(valueTable); diff --git a/src/main/msc/emfat_file.c b/src/main/msc/emfat_file.c index fdb8eb5f24..c1d5d69dc1 100644 --- a/src/main/msc/emfat_file.c +++ b/src/main/msc/emfat_file.c @@ -22,14 +22,18 @@ * Author: jflyper@github.com */ -#include "common/utils.h" -#include "common/printf.h" - +#include "platform.h" #include "emfat.h" #include "emfat_file.h" +#include "common/printf.h" +#include "common/time.h" +#include "common/utils.h" + #include "io/flashfs.h" +#include "msc/usbd_storage.h" + #define FILESYSTEM_SIZE_MB 256 #define USE_EMFAT_AUTORUN @@ -269,6 +273,14 @@ static emfat_entry_t entries[EMFAT_MAX_ENTRY]; static char logNames[EMFAT_MAX_LOG_ENTRY][8+3]; emfat_t emfat; +static uint32_t cmaTime = CMA_TIME; + +static void emfat_set_entry_cma(emfat_entry_t *entry) +{ + entry->cma_time[0] = cmaTime; + entry->cma_time[1] = cmaTime; + entry->cma_time[2] = cmaTime; +} static void emfat_add_log(emfat_entry_t *entry, int number, uint32_t offset, uint32_t size) { @@ -278,10 +290,8 @@ static void emfat_add_log(emfat_entry_t *entry, int number, uint32_t offset, uin entry->offset = offset; entry->curr_size = size; entry->max_size = entry->curr_size; - entry->cma_time[0] = CMA_TIME; - entry->cma_time[1] = CMA_TIME; - entry->cma_time[2] = CMA_TIME; entry->readcb = bblog_read_proc; + emfat_set_entry_cma(entry); } static int emfat_find_log(emfat_entry_t *entry, int maxCount) @@ -327,8 +337,17 @@ void emfat_init_files(void) emfat_entry_t *entry; memset(entries, 0, sizeof(entries)); +#ifdef USE_PERSISTENT_MSC_RTC + rtcTime_t mscRebootRtc; + if (rtcPersistRead(&mscRebootRtc)) { + const int32_t rtcSeconds = rtcTimeGetSeconds(&mscRebootRtc); + cmaTime = emfat_cma_time_from_unix((uint32_t)rtcSeconds); + } +#endif + for (size_t i = 0 ; i < PREDEFINED_ENTRY_COUNT ; i++) { entries[i] = entriesPredefined[i]; + emfat_set_entry_cma(&entries[i]); } // Detect and create entries for each individual log @@ -343,6 +362,7 @@ void emfat_init_files(void) entry = &entries[entryIndex]; entry->curr_size = flashfsIdentifyStartOfFreeSpace(); entry->max_size = entry->curr_size; + emfat_set_entry_cma(entry); ++entryIndex; } @@ -352,6 +372,7 @@ void emfat_init_files(void) // used space is doubled because of the individual files plus the single complete file entry->curr_size = (FILESYSTEM_SIZE_MB * 1024 * 1024) - (flashfsIdentifyStartOfFreeSpace() * 2); entry->max_size = entry->curr_size; + emfat_set_entry_cma(entry); emfat_init(&emfat, "BETAFLT", entries); } diff --git a/src/main/msc/usbd_storage.c b/src/main/msc/usbd_storage.c index 4312120699..769cf34841 100644 --- a/src/main/msc/usbd_storage.c +++ b/src/main/msc/usbd_storage.c @@ -21,6 +21,9 @@ /* * Author: jflyper (https://github.com/jflyper) */ + +#include "platform.h" +#include "common/time.h" #ifdef USE_HAL_DRIVER #include "usbd_msc.h" diff --git a/src/main/msc/usbd_storage.h b/src/main/msc/usbd_storage.h index f77d326cde..afc8c0925b 100644 --- a/src/main/msc/usbd_storage.h +++ b/src/main/msc/usbd_storage.h @@ -27,6 +27,8 @@ #include "usbd_msc_core.h" #endif +#include "common/time.h" + #ifdef USE_HAL_DRIVER extern USBD_StorageTypeDef *USBD_STORAGE_fops; #ifdef USE_SDCARD_SDIO diff --git a/src/main/target/common_post.h b/src/main/target/common_post.h index 726ac8c6ab..8fa15bf231 100644 --- a/src/main/target/common_post.h +++ b/src/main/target/common_post.h @@ -223,3 +223,7 @@ #define SPI_PREINIT_COUNT 16 // 2 x 8 (GYROx2, BARO, MAG, MAX, FLASHx2, RX) #endif #endif + +#if (!defined(USE_FLASHFS) || !defined(USE_RTC_TIME) || !defined(USE_USB_MSC)) +#undef USE_PERSISTENT_MSC_RTC +#endif diff --git a/src/main/target/common_pre.h b/src/main/target/common_pre.h index 7cdc119313..80e5be0b4e 100644 --- a/src/main/target/common_pre.h +++ b/src/main/target/common_pre.h @@ -59,6 +59,7 @@ #define USE_ADC_INTERNAL #define USE_USB_CDC_HID #define USE_USB_MSC +#define USE_PERSISTENT_MSC_RTC #if defined(STM32F40_41xxx) || defined(STM32F411xE) #define USE_OVERCLOCK @@ -78,6 +79,7 @@ #define USE_ADC_INTERNAL #define USE_USB_CDC_HID #define USE_USB_MSC +#define USE_PERSISTENT_MSC_RTC #define USE_MCO #endif diff --git a/src/test/unit/osd_unittest.cc b/src/test/unit/osd_unittest.cc index 1161c2bae1..86e2578674 100644 --- a/src/test/unit/osd_unittest.cc +++ b/src/test/unit/osd_unittest.cc @@ -37,6 +37,7 @@ extern "C" { #include "common/time.h" #include "drivers/max7456_symbols.h" + #include "drivers/persistent.h" #include "drivers/serial.h" #include "fc/config.h" @@ -1080,4 +1081,6 @@ extern "C" { bool failsafeIsActive(void) { return false; } bool gpsRescueIsConfigured(void) { return false; } int8_t calculateThrottlePercent(void) { return 0; } + uint32_t persistentObjectRead(persistentObjectId_e) { return 0; } + void persistentObjectWrite(persistentObjectId_e, uint32_t) {} }