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)
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/>.
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"
35 #include "config/config.h"
37 #ifdef CONFIG_IN_SDCARD
38 #include "io/asyncfatfs/asyncfatfs.h"
41 #include "drivers/flash.h"
42 #include "drivers/system.h"
44 static uint16_t eepromConfigSize
;
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.
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.
68 // lower 2 bits used to indicate system or profile number, see CR_CLASSIFICATION_MASK
72 } PG_PACKED configRecord_t
;
74 // Footer for the saved copy.
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.
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;
98 #ifdef CONFIG_IN_MEMORY_MAPPED_FLASH
99 flashMemoryMappedModeDisable();
102 bytesRead
= flashReadBytes(flashStartAddress
+ totalBytesRead
, &eepromData
[totalBytesRead
], EEPROM_SIZE
- totalBytesRead
);
104 totalBytesRead
+= bytesRead
;
105 success
= (totalBytesRead
== EEPROM_SIZE
);
107 } while (!success
&& bytesRead
> 0);
108 #ifdef CONFIG_IN_MEMORY_MAPPED_FLASH
109 flashMemoryMappedModeEnable();
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
;
129 flashMemoryMappedModeDisable();
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);
152 flashMemoryMappedModeEnable();
157 #elif defined(CONFIG_IN_SDCARD)
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
)
180 fileState
= FILE_STATE_FAILED
;
184 uint32_t totalBytesWritten
= 0;
185 uint32_t bytesWritten
= 0;
189 bytesWritten
= afatfs_fwrite(file
, &eepromData
[totalBytesWritten
], EEPROM_SIZE
- totalBytesWritten
);
190 totalBytesWritten
+= bytesWritten
;
191 success
= (totalBytesWritten
== EEPROM_SIZE
);
194 } while (!success
&& afatfs_getLastError() == AFATFS_ERROR_NONE
);
197 fileState
= FILE_STATE_FAILED
;
200 while (!afatfs_fclose(file
, saveEEPROMToSDCardCloseContinue
)) {
205 bool saveEEPROMToSDCard(void)
207 fileState
= FILE_STATE_BUSY
;
208 bool result
= afatfs_fopen(defaultSDCardConfigFilename
, "w+", saveEEPROMToSDCardWriteContinue
);
213 while (fileState
== FILE_STATE_BUSY
) {
217 while (!afatfs_flush()) {
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
)
234 fileState
= FILE_STATE_FAILED
;
238 fileState
= FILE_STATE_BUSY
;
240 uint32_t totalBytesRead
= 0;
241 uint32_t bytesRead
= 0;
244 if (afatfs_feof(file
)) {
245 // empty file, nothing to load.
246 memset(eepromData
, 0x00, EEPROM_SIZE
);
251 bytesRead
= afatfs_fread(file
, &eepromData
[totalBytesRead
], EEPROM_SIZE
- totalBytesRead
);
252 totalBytesRead
+= bytesRead
;
253 success
= (totalBytesRead
== EEPROM_SIZE
);
256 } while (!success
&& afatfs_getLastError() == AFATFS_ERROR_NONE
);
260 fileState
= FILE_STATE_FAILED
;
263 while (!afatfs_fclose(file
, loadEEPROMFromSDCardCloseContinue
)) {
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
);
279 while (fileState
== FILE_STATE_BUSY
) {
283 return (fileState
== FILE_STATE_COMPLETE
);
287 #ifdef CONFIG_IN_FILE
288 void loadEEPROMFromFile(void)
290 FLASH_Unlock(); // load existing config file into eepromData
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();
309 // Flash read failed - just die now
310 failureMode(FAILURE_FLASH_READ_FAILED
);
312 #elif defined(CONFIG_IN_SDCARD)
313 bool eepromLoaded
= loadEEPROMFromSDCard();
315 // SDCard read failed - just die now
316 failureMode(FAILURE_SDCARD_READ_FAILED
);
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
) {
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) {
343 uint16_t crc
= CRC_START_VALUE
;
344 crc
= crc16_ccitt_update(crc
, header
, sizeof(*header
));
345 p
+= sizeof(*header
);
348 const configRecord_t
*record
= (const configRecord_t
*)p
;
350 if (record
->size
== 0) {
351 // Found the end. Stop scanning.
354 if (p
+ record
->size
>= &__config_end
355 || record
->size
< sizeof(*record
)) {
356 // Too big or too small.
360 crc
= crc16_ccitt_update(crc
, 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
;
395 return &__config_end
- &__config_start
;
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
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
))
412 if (pgN(reg
) == record
->pgn
413 && (record
->flags
& CR_CLASSIFICATION_MASK
) == classification
)
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)
429 const configRecord_t
*rec
= findEEPROM(reg
, CR_CLASSICATION_SYSTEM
);
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
)) {
440 *reg
->fnv_hash
= fnv_update(FNV_OFFSET_BASIS
, reg
->address
, pgSize(reg
));
446 static bool writeSettingsToEEPROM(void)
448 bool dirtyConfig
= !isEEPROMVersionValid() || !isEEPROMStructureValid();
450 configHeader_t header
= {
451 .eepromConfigVersion
= EEPROM_CONF_VERSION
,
456 if (*reg
->fnv_hash
!= fnv_update(FNV_OFFSET_BASIS
, reg
->address
, pgSize(reg
))) {
461 // Only write the config if it has changed
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
));
472 const uint16_t regSize
= pgSize(reg
);
473 configRecord_t record
= {
474 .size
= sizeof(configRecord_t
) + regSize
,
476 .version
= pgVersion(reg
),
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
= {
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);
507 void writeConfigToEEPROM(void)
509 bool success
= false;
511 for (int attempt
= 0; attempt
< 3 && !success
; attempt
++) {
512 if (writeSettingsToEEPROM()) {
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();
519 #ifdef CONFIG_IN_SDCARD
520 // copy it back from flash to the in-memory buffer.
521 success
= loadEEPROMFromSDCard();
527 if (success
&& isEEPROMVersionValid() && isEEPROMStructureValid()) {
531 // Flash write failed - just die now
532 failureMode(FAILURE_CONFIG_STORE_FAILURE
);