diff --git a/src/main/common/crc.c b/src/main/common/crc.c index bbc4019177..f43b75ee7c 100644 --- a/src/main/common/crc.c +++ b/src/main/common/crc.c @@ -20,6 +20,8 @@ #include +#include "common/crc.h" + #include "platform.h" #include "streambuf.h" @@ -114,3 +116,17 @@ void crc8_xor_sbuf_append(sbuf_t *dst, uint8_t *start) sbufWriteU8(dst, crc); } +// Fowler–Noll–Vo hash function; see https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function +uint32_t fnv_update(uint32_t hash, const void *data, uint32_t length) +{ + const uint8_t *p = (const uint8_t *)data; + const uint8_t *pend = p + length; + + for (; p != pend; p++) { + hash *= FNV_PRIME; + hash ^= *p; + } + + return hash; +} + diff --git a/src/main/common/crc.h b/src/main/common/crc.h index dcd4fd9709..f795f31810 100644 --- a/src/main/common/crc.h +++ b/src/main/common/crc.h @@ -38,3 +38,8 @@ void crc8_sbuf_append(struct sbuf_s *dst, uint8_t *start, uint8_t poly); uint8_t crc8_xor_update(uint8_t crc, const void *data, uint32_t length); void crc8_xor_sbuf_append(struct sbuf_s *dst, uint8_t *start); + +#define FNV_PRIME 16777619 +#define FNV_OFFSET_BASIS 2166136261 + +uint32_t fnv_update(uint32_t hash, const void *data, uint32_t length); diff --git a/src/main/config/config_eeprom.c b/src/main/config/config_eeprom.c index 4f4055a4bb..ab8bef3f76 100644 --- a/src/main/config/config_eeprom.c +++ b/src/main/config/config_eeprom.c @@ -388,6 +388,7 @@ bool loadEEPROM(void) success = false; } + *reg->fnv_hash = fnv_update(FNV_OFFSET_BASIS, reg->address, pgSize(reg)); } return success; @@ -395,51 +396,63 @@ bool loadEEPROM(void) static bool writeSettingsToEEPROM(void) { - config_streamer_t streamer; - config_streamer_init(&streamer); - - config_streamer_start(&streamer, (uintptr_t)&__config_start, &__config_end - &__config_start); + bool dirtyConfig = !isEEPROMVersionValid() || !isEEPROMStructureValid(); configHeader_t header = { .eepromConfigVersion = EEPROM_CONF_VERSION, .magic_be = 0xBE, }; - config_streamer_write(&streamer, (uint8_t *)&header, sizeof(header)); - uint16_t crc = CRC_START_VALUE; - crc = crc16_ccitt_update(crc, (uint8_t *)&header, sizeof(header)); PG_FOREACH(reg) { - const uint16_t regSize = pgSize(reg); - configRecord_t record = { - .size = sizeof(configRecord_t) + regSize, - .pgn = pgN(reg), - .version = pgVersion(reg), - .flags = 0 - }; - - record.flags |= CR_CLASSICATION_SYSTEM; - config_streamer_write(&streamer, (uint8_t *)&record, sizeof(record)); - crc = crc16_ccitt_update(crc, (uint8_t *)&record, sizeof(record)); - config_streamer_write(&streamer, reg->address, regSize); - crc = crc16_ccitt_update(crc, reg->address, regSize); + if (*reg->fnv_hash != fnv_update(FNV_OFFSET_BASIS, reg->address, pgSize(reg))) { + dirtyConfig = true; + } } - configFooter_t footer = { - .terminator = 0, - }; + // Only write the config if it has changed + if (dirtyConfig) { + config_streamer_t streamer; + config_streamer_init(&streamer); - config_streamer_write(&streamer, (uint8_t *)&footer, sizeof(footer)); - crc = crc16_ccitt_update(crc, (uint8_t *)&footer, sizeof(footer)); + config_streamer_start(&streamer, (uintptr_t)&__config_start, &__config_end - &__config_start); - // include inverted CRC in big endian format in the CRC - const uint16_t invertedBigEndianCrc = ~(((crc & 0xFF) << 8) | (crc >> 8)); - config_streamer_write(&streamer, (uint8_t *)&invertedBigEndianCrc, sizeof(crc)); + config_streamer_write(&streamer, (uint8_t *)&header, sizeof(header)); + uint16_t crc = CRC_START_VALUE; + crc = crc16_ccitt_update(crc, (uint8_t *)&header, sizeof(header)); + PG_FOREACH(reg) { + const uint16_t regSize = pgSize(reg); + configRecord_t record = { + .size = sizeof(configRecord_t) + regSize, + .pgn = pgN(reg), + .version = pgVersion(reg), + .flags = 0, + }; - config_streamer_flush(&streamer); - const bool success = config_streamer_finish(&streamer) == 0; + record.flags |= CR_CLASSICATION_SYSTEM; + config_streamer_write(&streamer, (uint8_t *)&record, sizeof(record)); + crc = crc16_ccitt_update(crc, (uint8_t *)&record, sizeof(record)); + config_streamer_write(&streamer, reg->address, regSize); + crc = crc16_ccitt_update(crc, reg->address, regSize); + } - return success; + configFooter_t footer = { + .terminator = 0, + }; + + config_streamer_write(&streamer, (uint8_t *)&footer, sizeof(footer)); + crc = crc16_ccitt_update(crc, (uint8_t *)&footer, sizeof(footer)); + + // include inverted CRC in big endian format in the CRC + const uint16_t invertedBigEndianCrc = ~(((crc & 0xFF) << 8) | (crc >> 8)); + config_streamer_write(&streamer, (uint8_t *)&invertedBigEndianCrc, sizeof(crc)); + + config_streamer_flush(&streamer); + + return (config_streamer_finish(&streamer) == 0); + } else { + return true; + } } void writeConfigToEEPROM(void) diff --git a/src/main/pg/pg.c b/src/main/pg/pg.c index 3489d9192e..1da2fafc49 100644 --- a/src/main/pg/pg.c +++ b/src/main/pg/pg.c @@ -24,6 +24,7 @@ #include "platform.h" +#include "common/crc.h" #include "common/maths.h" #include "pg.h" @@ -80,6 +81,8 @@ bool pgLoad(const pgRegistry_t* reg, const void *from, int size, int version) const int take = MIN(size, pgSize(reg)); memcpy(pgOffset(reg), from, take); + *reg->fnv_hash = fnv_update(FNV_OFFSET_BASIS, from, take); + return true; } diff --git a/src/main/pg/pg.h b/src/main/pg/pg.h index 947aa3bf51..c2cef49a39 100644 --- a/src/main/pg/pg.h +++ b/src/main/pg/pg.h @@ -54,6 +54,7 @@ typedef struct pgRegistry_s { void *ptr; // Pointer to init template pgResetFunc *fn; // Popinter to pgResetFunc } reset; + uint32_t *fnv_hash; // Used to detect if config has changed prior to write } pgRegistry_t; static inline uint16_t pgN(const pgRegistry_t* reg) {return reg->pgn & PGR_PGN_MASK;} @@ -118,6 +119,7 @@ extern const uint8_t __pg_resetdata_end[]; #define PG_REGISTER_I(_type, _name, _pgn, _version, _reset) \ _type _name ## _System; \ _type _name ## _Copy; \ + uint32_t _name ## _fnv_hash; \ /* Force external linkage for g++. Catch multi registration */ \ extern const pgRegistry_t _name ## _Registry; \ const pgRegistry_t _name ##_Registry PG_REGISTER_ATTRIBUTES = { \ @@ -125,6 +127,7 @@ extern const uint8_t __pg_resetdata_end[]; .length = 1, \ .size = sizeof(_type) | PGR_SIZE_SYSTEM_FLAG, \ .address = (uint8_t*)&_name ## _System, \ + .fnv_hash = &_name ## _fnv_hash, \ .copy = (uint8_t*)&_name ## _Copy, \ .ptr = 0, \ _reset, \ @@ -149,12 +152,14 @@ extern const uint8_t __pg_resetdata_end[]; #define PG_REGISTER_ARRAY_I(_type, _length, _name, _pgn, _version, _reset) \ _type _name ## _SystemArray[_length]; \ _type _name ## _CopyArray[_length]; \ + uint32_t _name ## _fnv_hash; \ extern const pgRegistry_t _name ##_Registry; \ const pgRegistry_t _name ## _Registry PG_REGISTER_ATTRIBUTES = { \ .pgn = _pgn | (_version << 12), \ .length = _length, \ .size = (sizeof(_type) * _length) | PGR_SIZE_SYSTEM_FLAG, \ .address = (uint8_t*)&_name ## _SystemArray, \ + .fnv_hash = &_name ## _fnv_hash, \ .copy = (uint8_t*)&_name ## _CopyArray, \ .ptr = 0, \ _reset, \ diff --git a/src/test/Makefile b/src/test/Makefile index e8e77ac86a..e7a8a242f1 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -106,7 +106,9 @@ blackbox_encoding_unittest_SRC := \ cli_unittest_SRC := \ $(USER_DIR)/cli/cli.c \ + $(USER_DIR)/common/crc.c \ $(USER_DIR)/common/printf.c \ + $(USER_DIR)/common/streambuf.c \ $(USER_DIR)/config/feature.c \ $(USER_DIR)/pg/pg.c \ $(USER_DIR)/common/typeconversion.c @@ -227,6 +229,8 @@ link_quality_unittest_DEFINES := \ USE_RX_LINK_QUALITY_INFO= pg_unittest_SRC := \ + $(USER_DIR)/common/crc.c \ + $(USER_DIR)/common/streambuf.c \ $(USER_DIR)/pg/pg.c @@ -234,7 +238,9 @@ rc_controls_unittest_SRC := \ $(USER_DIR)/fc/rc_controls.c \ $(USER_DIR)/pg/pg.c \ $(USER_DIR)/common/bitarray.c \ + $(USER_DIR)/common/crc.c \ $(USER_DIR)/common/maths.c \ + $(USER_DIR)/common/streambuf.c \ $(USER_DIR)/fc/rc_adjustments.c \ $(USER_DIR)/fc/rc_modes.c @@ -254,7 +260,9 @@ rx_ibus_unittest_SRC := \ rx_ranges_unittest_SRC := \ $(USER_DIR)/common/bitarray.c \ + $(USER_DIR)/common/crc.c \ $(USER_DIR)/common/maths.c \ + $(USER_DIR)/common/streambuf.c \ $(USER_DIR)/fc/rc_modes.c \ $(USER_DIR)/rx/rx.c \ $(USER_DIR)/pg/pg.c \ @@ -287,7 +295,9 @@ sensor_gyro_unittest_SRC := \ $(USER_DIR)/sensors/gyro_init.c \ $(USER_DIR)/sensors/boardalignment.c \ $(USER_DIR)/common/filter.c \ + $(USER_DIR)/common/crc.c \ $(USER_DIR)/common/maths.c \ + $(USER_DIR)/common/streambuf.c \ $(USER_DIR)/common/sensor_alignment.c \ $(USER_DIR)/drivers/accgyro/accgyro_fake.c \ $(USER_DIR)/drivers/accgyro/gyro_sync.c \ @@ -378,8 +388,10 @@ rcdevice_unittest_SRC := \ $(USER_DIR)/pg/pg.c \ pid_unittest_SRC := \ + $(USER_DIR)/common/crc.c \ $(USER_DIR)/common/filter.c \ $(USER_DIR)/common/maths.c \ + $(USER_DIR)/common/streambuf.c \ $(USER_DIR)/drivers/accgyro/gyro_sync.c \ $(USER_DIR)/fc/controlrate_profile.c \ $(USER_DIR)/fc/runtime_config.c \