2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
25 #include "build/build_config.h"
27 #include "common/crc.h"
28 #include "common/utils.h"
30 #include "config/config_eeprom.h"
31 #include "config/config_streamer.h"
32 #include "config/parameter_group.h"
34 #include "drivers/system.h"
35 #include "drivers/flash.h"
37 #include "fc/config.h"
39 #if defined(CONFIG_IN_FILE)
40 void config_streamer_impl_unlock(void);
43 static uint16_t eepromConfigSize
;
46 CR_CLASSICATION_SYSTEM
= 0,
47 CR_CLASSICATION_PROFILE1
= 1,
48 CR_CLASSICATION_PROFILE2
= 2,
49 CR_CLASSICATION_PROFILE3
= 3,
50 CR_CLASSICATION_PROFILE_LAST
= CR_CLASSICATION_PROFILE3
,
51 } configRecordFlags_e
;
53 #define CR_CLASSIFICATION_MASK (0x3)
55 // Header for the saved copy.
58 } PG_PACKED configHeader_t
;
60 // Header for each stored PG.
67 // lower 2 bits used to indicate system or profile number, see CR_CLASSIFICATION_MASK
71 } PG_PACKED configRecord_t
;
73 // Footer for the saved copy.
76 } PG_PACKED configFooter_t
;
77 // checksum is appended just after footer. It is not included in footer to make checksum calculation consistent
79 // Used to check the compiler packing at build time.
83 } PG_PACKED packingTest_t
;
85 #if defined(CONFIG_IN_EXTERNAL_FLASH)
86 bool loadEEPROMFromExternalFlash(void)
88 const flashPartition_t
*flashPartition
= flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG
);
89 const flashGeometry_t
*flashGeometry
= flashGetGeometry();
91 uint32_t flashStartAddress
= flashPartition
->startSector
* flashGeometry
->sectorSize
;
93 uint32_t totalBytesRead
= 0;
99 bytesRead
= flashReadBytes(flashStartAddress
+ totalBytesRead
, &eepromData
[totalBytesRead
], EEPROM_SIZE
- totalBytesRead
);
101 totalBytesRead
+= bytesRead
;
102 success
= (totalBytesRead
== EEPROM_SIZE
);
104 } while (!success
&& bytesRead
> 0);
108 #endif /* defined(CONFIG_IN_EXTERNAL_FLASH) */
110 void initEEPROM(void)
112 // Verify that this architecture packs as expected.
113 BUILD_BUG_ON(offsetof(packingTest_t
, byte
) != 0);
114 BUILD_BUG_ON(offsetof(packingTest_t
, word
) != 1);
115 BUILD_BUG_ON(sizeof(packingTest_t
) != 5);
117 BUILD_BUG_ON(sizeof(configHeader_t
) != 1);
118 BUILD_BUG_ON(sizeof(configFooter_t
) != 2);
119 BUILD_BUG_ON(sizeof(configRecord_t
) != 6);
121 #if defined(CONFIG_IN_EXTERNAL_FLASH)
122 bool eepromLoaded
= loadEEPROMFromExternalFlash();
124 // Flash read failed - just die now
125 failureMode(FAILURE_FLASH_READ_FAILED
);
127 #elif defined(CONFIG_IN_FILE)
128 config_streamer_impl_unlock();
132 // Scan the EEPROM config. Returns true if the config is valid.
133 bool isEEPROMContentValid(void)
135 const uint8_t *p
= &__config_start
;
136 const configHeader_t
*header
= (const configHeader_t
*)p
;
138 if (header
->format
!= EEPROM_CONF_VERSION
) {
141 uint16_t crc
= crc16_ccitt_update(0, header
, sizeof(*header
));
142 p
+= sizeof(*header
);
145 const configRecord_t
*record
= (const configRecord_t
*)p
;
147 if (record
->size
== 0) {
148 // Found the end. Stop scanning.
152 if (p
+ sizeof(*record
) >= &__config_end
) {
153 // Too big. Further checking for size doesn't make sense
157 if (p
+ record
->size
>= &__config_end
|| record
->size
< sizeof(*record
)) {
158 // Too big or too small.
162 crc
= crc16_ccitt_update(crc
, p
, record
->size
);
167 const configFooter_t
*footer
= (const configFooter_t
*)p
;
168 crc
= crc16_ccitt_update(crc
, footer
, sizeof(*footer
));
169 p
+= sizeof(*footer
);
170 const uint16_t checkSum
= *(uint16_t *)p
;
171 p
+= sizeof(checkSum
);
172 eepromConfigSize
= p
- &__config_start
;
173 return crc
== checkSum
;
176 uint16_t getEEPROMConfigSize(void)
178 return eepromConfigSize
;
181 // find config record for reg + classification (profile info) in EEPROM
182 // return NULL when record is not found
183 // this function assumes that EEPROM content is valid
184 static const configRecord_t
*findEEPROM(const pgRegistry_t
*reg
, configRecordFlags_e classification
)
186 const uint8_t *p
= &__config_start
;
187 p
+= sizeof(configHeader_t
); // skip header
189 const configRecord_t
*record
= (const configRecord_t
*)p
;
190 // Ensure that the record header fits into config memory, otherwise accessing size and flags may cause a hardfault.
191 if (p
+ sizeof(*record
) >= &__config_end
) {
195 // Check that record header makes sense
196 if (record
->size
== 0 || p
+ record
->size
>= &__config_end
|| record
->size
< sizeof(*record
)) {
200 // Check if this is the record we're looking for (check for size)
201 if (pgN(reg
) == record
->pgn
&& (record
->flags
& CR_CLASSIFICATION_MASK
) == classification
) {
211 // Initialize all PG records from EEPROM.
212 // This functions processes all PGs sequentially, scanning EEPROM for each one. This is suboptimal,
213 // but each PG is loaded/initialized exactly once and in defined order.
214 bool loadEEPROM(void)
217 configRecordFlags_e cls_start
, cls_end
;
218 if (pgIsSystem(reg
)) {
219 cls_start
= CR_CLASSICATION_SYSTEM
;
220 cls_end
= CR_CLASSICATION_SYSTEM
;
222 cls_start
= CR_CLASSICATION_PROFILE1
;
223 cls_end
= CR_CLASSICATION_PROFILE_LAST
;
225 for (configRecordFlags_e cls
= cls_start
; cls
<= cls_end
; cls
++) {
226 int profileIndex
= cls
- cls_start
;
227 const configRecord_t
*rec
= findEEPROM(reg
, cls
);
229 // config from EEPROM is available, use it to initialize PG. pgLoad will handle version mismatch
230 pgLoad(reg
, profileIndex
, rec
->pg
, rec
->size
- offsetof(configRecord_t
, pg
), rec
->version
);
232 pgReset(reg
, profileIndex
);
239 static bool writeSettingsToEEPROM(void)
241 config_streamer_t streamer
;
242 config_streamer_init(&streamer
);
244 config_streamer_start(&streamer
, (uintptr_t)&__config_start
, &__config_end
- &__config_start
);
246 configHeader_t header
= {
247 .format
= EEPROM_CONF_VERSION
,
250 if (config_streamer_write(&streamer
, (uint8_t *)&header
, sizeof(header
)) < 0) {
253 uint16_t crc
= crc16_ccitt_update(0, (uint8_t *)&header
, sizeof(header
));
255 const uint16_t regSize
= pgSize(reg
);
256 configRecord_t record
= {
257 .size
= sizeof(configRecord_t
) + regSize
,
259 .version
= pgVersion(reg
),
263 if (pgIsSystem(reg
)) {
264 // write the only instance
265 record
.flags
|= CR_CLASSICATION_SYSTEM
;
266 if (config_streamer_write(&streamer
, (uint8_t *)&record
, sizeof(record
)) < 0) {
269 crc
= crc16_ccitt_update(crc
, (uint8_t *)&record
, sizeof(record
));
270 if (config_streamer_write(&streamer
, reg
->address
, regSize
) < 0) {
273 crc
= crc16_ccitt_update(crc
, reg
->address
, regSize
);
275 // write one instance for each profile
276 for (uint8_t profileIndex
= 0; profileIndex
< MAX_PROFILE_COUNT
; profileIndex
++) {
279 record
.flags
|= ((profileIndex
+ 1) & CR_CLASSIFICATION_MASK
);
280 if (config_streamer_write(&streamer
, (uint8_t *)&record
, sizeof(record
)) < 0) {
283 crc
= crc16_ccitt_update(crc
, (uint8_t *)&record
, sizeof(record
));
284 const uint8_t *address
= reg
->address
+ (regSize
* profileIndex
);
285 if (config_streamer_write(&streamer
, address
, regSize
) < 0) {
288 crc
= crc16_ccitt_update(crc
, address
, regSize
);
293 configFooter_t footer
= {
297 if (config_streamer_write(&streamer
, (uint8_t *)&footer
, sizeof(footer
)) < 0) {
300 crc
= crc16_ccitt_update(crc
, (uint8_t *)&footer
, sizeof(footer
));
302 // append checksum now
303 if (config_streamer_write(&streamer
, (uint8_t *)&crc
, sizeof(crc
)) < 0) {
307 if (config_streamer_flush(&streamer
) < 0) {
311 bool success
= config_streamer_finish(&streamer
) == 0;
316 void writeConfigToEEPROM(void)
318 bool success
= false;
320 for (int attempt
= 0; attempt
< 3 && !success
; attempt
++) {
321 if (writeSettingsToEEPROM()) {
323 #ifdef CONFIG_IN_EXTERNAL_FLASH
324 // copy it back from flash to the in-memory buffer.
325 success
= loadEEPROMFromExternalFlash();
330 if (success
&& isEEPROMContentValid()) {
334 // Flash write failed - just die now
335 failureMode(FAILURE_FLASH_WRITE_FAILED
);