[4.4.2] Remove 15 m/s limit on estimated vario (#12788)
[betaflight.git] / src / main / config / config_eeprom.c
blob1e166cab7b72a0a4ebefd7f08f1215f60d595f7a
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
25 #include "platform.h"
27 #include "build/build_config.h"
29 #include "common/crc.h"
30 #include "common/utils.h"
32 #include "config/config_eeprom.h"
33 #include "config/config_streamer.h"
34 #include "pg/pg.h"
35 #include "config/config.h"
37 #ifdef CONFIG_IN_SDCARD
38 #include "io/asyncfatfs/asyncfatfs.h"
39 #endif
41 #include "drivers/flash.h"
42 #include "drivers/system.h"
44 static uint16_t eepromConfigSize;
46 typedef enum {
47 CR_CLASSICATION_SYSTEM = 0,
48 CR_CLASSICATION_PROFILE_LAST = CR_CLASSICATION_SYSTEM,
49 } configRecordFlags_e;
51 #define CR_CLASSIFICATION_MASK (0x3)
52 #define CRC_START_VALUE 0xFFFF
53 #define CRC_CHECK_VALUE 0x1D0F // pre-calculated value of CRC that includes the CRC itself
55 // Header for the saved copy.
56 typedef struct {
57 uint8_t eepromConfigVersion;
58 uint8_t magic_be; // magic number, should be 0xBE
59 } PG_PACKED configHeader_t;
61 // Header for each stored PG.
62 typedef struct {
63 // split up.
64 uint16_t size;
65 pgn_t pgn;
66 uint8_t version;
68 // lower 2 bits used to indicate system or profile number, see CR_CLASSIFICATION_MASK
69 uint8_t flags;
71 uint8_t pg[];
72 } PG_PACKED configRecord_t;
74 // Footer for the saved copy.
75 typedef struct {
76 uint16_t terminator;
77 } PG_PACKED configFooter_t;
78 // checksum is appended just after footer. It is not included in footer to make checksum calculation consistent
80 // Used to check the compiler packing at build time.
81 typedef struct {
82 uint8_t byte;
83 uint32_t word;
84 } PG_PACKED packingTest_t;
86 #if defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH)
87 MMFLASH_CODE bool loadEEPROMFromExternalFlash(void)
89 const flashPartition_t *flashPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG);
90 const flashGeometry_t *flashGeometry = flashGetGeometry();
92 uint32_t flashStartAddress = flashPartition->startSector * flashGeometry->sectorSize;
94 uint32_t totalBytesRead = 0;
95 int bytesRead = 0;
97 bool success = false;
98 #ifdef CONFIG_IN_MEMORY_MAPPED_FLASH
99 flashMemoryMappedModeDisable();
100 #endif
101 do {
102 bytesRead = flashReadBytes(flashStartAddress + totalBytesRead, &eepromData[totalBytesRead], EEPROM_SIZE - totalBytesRead);
103 if (bytesRead > 0) {
104 totalBytesRead += bytesRead;
105 success = (totalBytesRead == EEPROM_SIZE);
107 } while (!success && bytesRead > 0);
108 #ifdef CONFIG_IN_MEMORY_MAPPED_FLASH
109 flashMemoryMappedModeEnable();
110 #endif
112 return success;
115 #ifdef CONFIG_IN_MEMORY_MAPPED_FLASH
116 MMFLASH_CODE_NOINLINE void saveEEPROMToMemoryMappedFlash(void)
118 const flashPartition_t *flashPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG);
119 const flashGeometry_t *flashGeometry = flashGetGeometry();
121 uint32_t flashSectorSize = flashGeometry->sectorSize;
122 uint32_t flashPageSize = flashGeometry->pageSize;
124 uint32_t flashStartAddress = flashPartition->startSector * flashGeometry->sectorSize;
126 uint32_t bytesRemaining = EEPROM_SIZE;
127 uint32_t offset = 0;
129 flashMemoryMappedModeDisable();
131 do {
132 uint32_t flashAddress = flashStartAddress + offset;
134 uint32_t bytesToWrite = bytesRemaining;
135 if (bytesToWrite > flashPageSize) {
136 bytesToWrite = flashPageSize;
139 bool onSectorBoundary = flashAddress % flashSectorSize == 0;
140 if (onSectorBoundary) {
141 flashEraseSector(flashAddress);
144 flashPageProgram(flashAddress, (uint8_t *)&eepromData[offset], bytesToWrite, NULL);
146 bytesRemaining -= bytesToWrite;
147 offset += bytesToWrite;
148 } while (bytesRemaining > 0);
150 flashWaitForReady();
152 flashMemoryMappedModeEnable();
154 #endif
157 #elif defined(CONFIG_IN_SDCARD)
159 enum {
160 FILE_STATE_NONE = 0,
161 FILE_STATE_BUSY = 1,
162 FILE_STATE_FAILED,
163 FILE_STATE_COMPLETE,
166 uint8_t fileState = FILE_STATE_NONE;
168 const char *defaultSDCardConfigFilename = "CONFIG.BIN";
170 void saveEEPROMToSDCardCloseContinue(void)
172 if (fileState != FILE_STATE_FAILED) {
173 fileState = FILE_STATE_COMPLETE;
177 void saveEEPROMToSDCardWriteContinue(afatfsFilePtr_t file)
179 if (!file) {
180 fileState = FILE_STATE_FAILED;
181 return;
184 uint32_t totalBytesWritten = 0;
185 uint32_t bytesWritten = 0;
186 bool success;
188 do {
189 bytesWritten = afatfs_fwrite(file, &eepromData[totalBytesWritten], EEPROM_SIZE - totalBytesWritten);
190 totalBytesWritten += bytesWritten;
191 success = (totalBytesWritten == EEPROM_SIZE);
193 afatfs_poll();
194 } while (!success && afatfs_getLastError() == AFATFS_ERROR_NONE);
196 if (!success) {
197 fileState = FILE_STATE_FAILED;
200 while (!afatfs_fclose(file, saveEEPROMToSDCardCloseContinue)) {
201 afatfs_poll();
205 bool saveEEPROMToSDCard(void)
207 fileState = FILE_STATE_BUSY;
208 bool result = afatfs_fopen(defaultSDCardConfigFilename, "w+", saveEEPROMToSDCardWriteContinue);
209 if (!result) {
210 return false;
213 while (fileState == FILE_STATE_BUSY) {
214 afatfs_poll();
217 while (!afatfs_flush()) {
218 afatfs_poll();
221 return (fileState == FILE_STATE_COMPLETE);
224 void loadEEPROMFromSDCardCloseContinue(void)
226 if (fileState != FILE_STATE_FAILED) {
227 fileState = FILE_STATE_COMPLETE;
231 void loadEEPROMFromSDCardReadContinue(afatfsFilePtr_t file)
233 if (!file) {
234 fileState = FILE_STATE_FAILED;
235 return;
238 fileState = FILE_STATE_BUSY;
240 uint32_t totalBytesRead = 0;
241 uint32_t bytesRead = 0;
242 bool success;
244 if (afatfs_feof(file)) {
245 // empty file, nothing to load.
246 memset(eepromData, 0x00, EEPROM_SIZE);
247 success = true;
248 } else {
250 do {
251 bytesRead = afatfs_fread(file, &eepromData[totalBytesRead], EEPROM_SIZE - totalBytesRead);
252 totalBytesRead += bytesRead;
253 success = (totalBytesRead == EEPROM_SIZE);
255 afatfs_poll();
256 } while (!success && afatfs_getLastError() == AFATFS_ERROR_NONE);
259 if (!success) {
260 fileState = FILE_STATE_FAILED;
263 while (!afatfs_fclose(file, loadEEPROMFromSDCardCloseContinue)) {
264 afatfs_poll();
267 return;
270 bool loadEEPROMFromSDCard(void)
272 fileState = FILE_STATE_BUSY;
273 // 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.
274 bool result = afatfs_fopen(defaultSDCardConfigFilename, "w+", loadEEPROMFromSDCardReadContinue);
275 if (!result) {
276 return false;
279 while (fileState == FILE_STATE_BUSY) {
280 afatfs_poll();
283 return (fileState == FILE_STATE_COMPLETE);
285 #endif
287 #ifdef CONFIG_IN_FILE
288 void loadEEPROMFromFile(void)
290 FLASH_Unlock(); // load existing config file into eepromData
292 #endif
294 void initEEPROM(void)
296 // Verify that this architecture packs as expected.
297 STATIC_ASSERT(offsetof(packingTest_t, byte) == 0, byte_packing_test_failed);
298 STATIC_ASSERT(offsetof(packingTest_t, word) == 1, word_packing_test_failed);
299 STATIC_ASSERT(sizeof(packingTest_t) == 5, overall_packing_test_failed);
301 STATIC_ASSERT(sizeof(configFooter_t) == 2, footer_size_failed);
302 STATIC_ASSERT(sizeof(configRecord_t) == 6, record_size_failed);
304 #if defined(CONFIG_IN_FILE)
305 loadEEPROMFromFile();
306 #elif defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH)
307 bool eepromLoaded = loadEEPROMFromExternalFlash();
308 if (!eepromLoaded) {
309 // Flash read failed - just die now
310 failureMode(FAILURE_FLASH_READ_FAILED);
312 #elif defined(CONFIG_IN_SDCARD)
313 bool eepromLoaded = loadEEPROMFromSDCard();
314 if (!eepromLoaded) {
315 // SDCard read failed - just die now
316 failureMode(FAILURE_SDCARD_READ_FAILED);
318 #endif
321 bool isEEPROMVersionValid(void)
323 const uint8_t *p = &__config_start;
324 const configHeader_t *header = (const configHeader_t *)p;
326 if (header->eepromConfigVersion != EEPROM_CONF_VERSION) {
327 return false;
330 return true;
333 // Scan the EEPROM config. Returns true if the config is valid.
334 bool isEEPROMStructureValid(void)
336 const uint8_t *p = &__config_start;
337 const configHeader_t *header = (const configHeader_t *)p;
339 if (header->magic_be != 0xBE) {
340 return false;
343 uint16_t crc = CRC_START_VALUE;
344 crc = crc16_ccitt_update(crc, header, sizeof(*header));
345 p += sizeof(*header);
347 for (;;) {
348 const configRecord_t *record = (const configRecord_t *)p;
350 if (record->size == 0) {
351 // Found the end. Stop scanning.
352 break;
354 if (p + record->size >= &__config_end
355 || record->size < sizeof(*record)) {
356 // Too big or too small.
357 return false;
360 crc = crc16_ccitt_update(crc, p, record->size);
362 p += record->size;
365 const configFooter_t *footer = (const configFooter_t *)p;
366 crc = crc16_ccitt_update(crc, footer, sizeof(*footer));
367 p += sizeof(*footer);
369 // include stored CRC in the CRC calculation
370 const uint16_t *storedCrc = (const uint16_t *)p;
371 crc = crc16_ccitt_update(crc, storedCrc, sizeof(*storedCrc));
372 p += sizeof(storedCrc);
374 eepromConfigSize = p - &__config_start;
376 // CRC has the property that if the CRC itself is included in the calculation the resulting CRC will have constant value
377 return crc == CRC_CHECK_VALUE;
380 uint16_t getEEPROMConfigSize(void)
382 return eepromConfigSize;
385 size_t getEEPROMStorageSize(void)
387 #if defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH)
389 const flashPartition_t *flashPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG);
390 return FLASH_PARTITION_SECTOR_COUNT(flashPartition) * flashGetGeometry()->sectorSize;
391 #endif
392 #ifdef CONFIG_IN_RAM
393 return EEPROM_SIZE;
394 #else
395 return &__config_end - &__config_start;
396 #endif
399 // find config record for reg + classification (profile info) in EEPROM
400 // return NULL when record is not found
401 // this function assumes that EEPROM content is valid
402 static const configRecord_t *findEEPROM(const pgRegistry_t *reg, configRecordFlags_e classification)
404 const uint8_t *p = &__config_start;
405 p += sizeof(configHeader_t); // skip header
406 while (true) {
407 const configRecord_t *record = (const configRecord_t *)p;
408 if (record->size == 0
409 || p + record->size >= &__config_end
410 || record->size < sizeof(*record))
411 break;
412 if (pgN(reg) == record->pgn
413 && (record->flags & CR_CLASSIFICATION_MASK) == classification)
414 return record;
415 p += record->size;
417 // record not found
418 return NULL;
421 // Initialize all PG records from EEPROM.
422 // This functions processes all PGs sequentially, scanning EEPROM for each one. This is suboptimal,
423 // but each PG is loaded/initialized exactly once and in defined order.
424 bool loadEEPROM(void)
426 bool success = true;
428 PG_FOREACH(reg) {
429 const configRecord_t *rec = findEEPROM(reg, CR_CLASSICATION_SYSTEM);
430 if (rec) {
431 // config from EEPROM is available, use it to initialize PG. pgLoad will handle version mismatch
432 if (!pgLoad(reg, rec->pg, rec->size - offsetof(configRecord_t, pg), rec->version)) {
433 success = false;
435 } else {
436 pgReset(reg);
438 success = false;
440 *reg->fnv_hash = fnv_update(FNV_OFFSET_BASIS, reg->address, pgSize(reg));
443 return success;
446 static bool writeSettingsToEEPROM(void)
448 bool dirtyConfig = !isEEPROMVersionValid() || !isEEPROMStructureValid();
450 configHeader_t header = {
451 .eepromConfigVersion = EEPROM_CONF_VERSION,
452 .magic_be = 0xBE,
455 PG_FOREACH(reg) {
456 if (*reg->fnv_hash != fnv_update(FNV_OFFSET_BASIS, reg->address, pgSize(reg))) {
457 dirtyConfig = true;
461 // Only write the config if it has changed
462 if (dirtyConfig) {
463 config_streamer_t streamer;
464 config_streamer_init(&streamer);
466 config_streamer_start(&streamer, (uintptr_t)&__config_start, &__config_end - &__config_start);
468 config_streamer_write(&streamer, (uint8_t *)&header, sizeof(header));
469 uint16_t crc = CRC_START_VALUE;
470 crc = crc16_ccitt_update(crc, (uint8_t *)&header, sizeof(header));
471 PG_FOREACH(reg) {
472 const uint16_t regSize = pgSize(reg);
473 configRecord_t record = {
474 .size = sizeof(configRecord_t) + regSize,
475 .pgn = pgN(reg),
476 .version = pgVersion(reg),
477 .flags = 0,
481 record.flags |= CR_CLASSICATION_SYSTEM;
482 config_streamer_write(&streamer, (uint8_t *)&record, sizeof(record));
483 crc = crc16_ccitt_update(crc, (uint8_t *)&record, sizeof(record));
484 config_streamer_write(&streamer, reg->address, regSize);
485 crc = crc16_ccitt_update(crc, reg->address, regSize);
488 configFooter_t footer = {
489 .terminator = 0,
492 config_streamer_write(&streamer, (uint8_t *)&footer, sizeof(footer));
493 crc = crc16_ccitt_update(crc, (uint8_t *)&footer, sizeof(footer));
495 // include inverted CRC in big endian format in the CRC
496 const uint16_t invertedBigEndianCrc = ~(((crc & 0xFF) << 8) | (crc >> 8));
497 config_streamer_write(&streamer, (uint8_t *)&invertedBigEndianCrc, sizeof(crc));
499 config_streamer_flush(&streamer);
501 return (config_streamer_finish(&streamer) == 0);
502 } else {
503 return true;
507 void writeConfigToEEPROM(void)
509 bool success = false;
510 // write it
511 for (int attempt = 0; attempt < 3 && !success; attempt++) {
512 if (writeSettingsToEEPROM()) {
513 success = true;
515 #if defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH)
516 // copy it back from flash to the in-memory buffer.
517 success = loadEEPROMFromExternalFlash();
518 #endif
519 #ifdef CONFIG_IN_SDCARD
520 // copy it back from flash to the in-memory buffer.
521 success = loadEEPROMFromSDCard();
522 #endif
527 if (success && isEEPROMVersionValid() && isEEPROMStructureValid()) {
528 return;
531 // Flash write failed - just die now
532 failureMode(FAILURE_CONFIG_STORE_FAILURE);