123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768 |
- // Copyright 2022 Nick Brassel (@tzarc)
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include <stdbool.h>
- #include "fnv.h"
- #include "wear_leveling.h"
- #include "wear_leveling_internal.h"
- /*
- This wear leveling algorithm is adapted from algorithms from previous
- implementations in QMK, namely:
- - Artur F. (http://engsta.com/stm32-flash-memory-eeprom-emulator/)
- - Yiancar -- QMK's base implementation for STM32F303
- - Ilya Zhuravlev -- initial wear leveling algorithm
- - Don Kjer -- increased flash density algorithm
- - Nick Brassel (@tzarc) -- decoupled for use on other peripherals
- At this layer, it is assumed that any reads/writes from the backing store
- have a "reset state" after erasure of zero.
- It is up to the backing store to perform translation of values, such as
- taking the complement in order to deal with flash memory's reset value.
- Terminology:
- - Backing store: this is the storage area used by the wear leveling
- algorithm.
- - Backing size: this is the amount of storage provided by the backing
- store for use by the wear leveling algorithm.
- - Backing write size: this is the minimum number of bytes the backing
- store can write in a single operation.
- - Logical data: this is the externally-visible "emulated EEPROM" that
- external subsystems "see" when performing reads/writes.
- - Logical size: this is the amount of storage available for use
- externally. Effectively, the "size of the EEPROM".
- - Write log: this is a section of the backing store used to keep track
- of modifications without overwriting existing data. This log is
- "played back" on startup such that any subsequent reads are capable
- of returning the latest data.
- - Consolidated data: this is a section of the backing store reserved for
- use for the latest copy of logical data. This is only ever written
- when the write log is full -- the latest values for the logical data
- are written here and the write log is cleared.
- Configurables:
- - BACKING_STORE_WRITE_SIZE: The number of bytes requires for a write
- operation. This is defined by the capabilities of the backing store.
- - WEAR_LEVELING_BACKING_SIZE: The number of bytes provided by the
- backing store for use by the wear leveling algorithm. This is
- defined by the capabilities of the backing store. This value must
- also be at least twice the size of the logical size, as well as a
- multiple of the logical size.
- - WEAR_LEVELING_LOGICAL_SIZE: The number of bytes externally visible
- to other subsystems performing reads/writes. This must be a multiple
- of the write size.
- General algorithm:
- During initialization:
- * The contents of the consolidated data section are read into cache.
- * The contents of the write log are "played back" and update the
- cache accordingly.
- During reads:
- * Logical data is served from the cache.
- During writes:
- * The cache is updated with the new data.
- * A new write log entry is appended to the log.
- * If the log's full, data is consolidated and the write log cleared.
- Write log structure:
- The first 8 bytes of the write log are a FNV1a_64 hash of the contents
- of the consolidated data area, in an attempt to detect and guard against
- any data corruption.
- The write log follows the hash:
- Given that the algorithm needs to cater for 2-, 4-, and 8-byte writes,
- a variable-length write log entry is used such that the minimal amount
- of storage is used based off the backing store write size.
- Firstly, an empty log entry is expected to be all zeros. If the backing
- store uses 0xFF for cleared bytes, it should return the complement, such
- that this wear-leveling algorithm "receives" zeros.
- For multi-byte writes, up to 8 bytes will be used for each log entry,
- depending on the size of backing store writes:
- ╔ Multi-byte Log Entry (2, 4-byte) ═╗
- ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║
- ║ └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║
- ║ LenAdd║ Address║ Address║Value[0]║
- ╚════════╩════════╩════════╩════════╝
- ╔ Multi-byte Log Entry (2-byte) ══════════════════════╗
- ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║BBBBBBBB║CCCCCCCC║
- ║ └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║
- ║ LenAdd║ Address║ Address║Value[0]║Value[1]║Value[2]║
- ╚════════╩════════╩════════╩════════╩════════╩════════╝
- ╔ Multi-byte Log Entry (2, 4, 8-byte) ══════════════════════════════════╗
- ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║BBBBBBBB║CCCCCCCC║DDDDDDDD║EEEEEEEE║
- ║ └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║
- ║ LenAdd║ Address║ Address║Value[0]║Value[1]║Value[2]║Value[3]║Value[4]║
- ╚════════╩════════╩════════╩════════╩════════╩════════╩════════╩════════╝
- 19 bits are used for the address, which allows for a max logical size of
- 512kB. Up to 5 bytes can be included in a single log entry.
- For 2-byte backing store writes, the last two bytes are optional
- depending on the length of data to be written. Accordingly, either 3
- or 4 backing store write operations will occur.
- For 4-byte backing store writes, either one or two write operations
- occur, depending on the length.
- For 8-byte backing store writes, one write operation occur.
- 2-byte backing store optimizations:
- For single byte writes, addresses between 0...63 are encoded in a single
- backing store write operation. 4- and 8-byte backing stores do not have
- this optimization as it does not minimize the number of bytes written.
- ╔ Byte-Entry ════╗
- ║01XXXXXXYYYYYYYY║
- ║ └─┬──┘└──┬───┘║
- ║ Address Value ║
- ╚════════════════╝
- 0 <= Address < 0x40 (64)
- A second optimization takes into account uint16_t writes of 0 or 1,
- specifically catering for KC_NO and KC_TRANSPARENT in the dynamic keymap
- subsystem. This is valid only for the first 16kB of logical data --
- addresses outside this range will use the multi-byte encoding above.
- ╔ U16-Encoded 0 ═╗
- ║100XXXXXXXXXXXXX║
- ║ │└─────┬─────┘║
- ║ │Address >> 1 ║
- ║ └── Value: 0 ║
- ╚════════════════╝
- 0 <= Address <= 0x3FFE (16382)
- ╔ U16-Encoded 1 ═╗
- ║101XXXXXXXXXXXXX║
- ║ │└─────┬─────┘║
- ║ │Address >> 1 ║
- ║ └── Value: 1 ║
- ╚════════════════╝
- 0 <= Address <= 0x3FFE (16382) */
- /**
- * Storage area for the wear-leveling cache.
- */
- static struct __attribute__((__aligned__(BACKING_STORE_WRITE_SIZE))) {
- __attribute__((__aligned__(BACKING_STORE_WRITE_SIZE))) uint8_t cache[(WEAR_LEVELING_LOGICAL_SIZE)];
- uint32_t write_address;
- bool unlocked;
- } wear_leveling;
- /**
- * Locking helper: status
- */
- typedef enum backing_store_lock_status_t { STATUS_FAILURE = 0, STATUS_SUCCESS, STATUS_UNCHANGED } backing_store_lock_status_t;
- /**
- * Locking helper: unlock
- */
- static inline backing_store_lock_status_t wear_leveling_unlock(void) {
- if (wear_leveling.unlocked) {
- return STATUS_UNCHANGED;
- }
- if (!backing_store_unlock()) {
- return STATUS_FAILURE;
- }
- wear_leveling.unlocked = true;
- return STATUS_SUCCESS;
- }
- /**
- * Locking helper: lock
- */
- static inline backing_store_lock_status_t wear_leveling_lock(void) {
- if (!wear_leveling.unlocked) {
- return STATUS_UNCHANGED;
- }
- if (!backing_store_lock()) {
- return STATUS_FAILURE;
- }
- wear_leveling.unlocked = false;
- return STATUS_SUCCESS;
- }
- /**
- * Resets the cache, ensuring the write address is correctly initialised.
- */
- static void wear_leveling_clear_cache(void) {
- memset(wear_leveling.cache, 0, (WEAR_LEVELING_LOGICAL_SIZE));
- wear_leveling.write_address = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 is due to the FNV1a_64 of the consolidated buffer
- }
- /**
- * Reads the consolidated data from the backing store into the cache.
- * Does not consider the write log.
- */
- static wear_leveling_status_t wear_leveling_read_consolidated(void) {
- wl_dprintf("Reading consolidated data\n");
- wear_leveling_status_t status = WEAR_LEVELING_SUCCESS;
- if (!backing_store_read_bulk(0, (backing_store_int_t *)wear_leveling.cache, sizeof(wear_leveling.cache) / sizeof(backing_store_int_t))) {
- wl_dprintf("Failed to read from backing store\n");
- status = WEAR_LEVELING_FAILED;
- }
- // Verify the FNV1a_64 result
- if (status != WEAR_LEVELING_FAILED) {
- uint64_t expected = fnv_64a_buf(wear_leveling.cache, (WEAR_LEVELING_LOGICAL_SIZE), FNV1A_64_INIT);
- write_log_entry_t entry;
- wl_dprintf("Reading checksum\n");
- #if BACKING_STORE_WRITE_SIZE == 2
- backing_store_read_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw16, 4);
- #elif BACKING_STORE_WRITE_SIZE == 4
- backing_store_read_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw32, 2);
- #elif BACKING_STORE_WRITE_SIZE == 8
- backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 0, &entry.raw64);
- #endif
- // If we have a mismatch, clear the cache but do not flag a failure,
- // which will cater for the completely clean MCU case.
- if (entry.raw64 == expected) {
- wl_dprintf("Checksum matches, consolidated data is correct\n");
- } else {
- wl_dprintf("Checksum mismatch, clearing cache\n");
- wear_leveling_clear_cache();
- }
- }
- // If we failed for any reason, then clear the cache
- if (status == WEAR_LEVELING_FAILED) {
- wear_leveling_clear_cache();
- }
- return status;
- }
- /**
- * Writes the current cache to consolidated data at the beginning of the backing store.
- * Does not clear the write log.
- * Pre-condition: this is just after an erase, so we can write directly without reading.
- */
- static wear_leveling_status_t wear_leveling_write_consolidated(void) {
- wl_dprintf("Writing consolidated data\n");
- backing_store_lock_status_t lock_status = wear_leveling_unlock();
- wear_leveling_status_t status = WEAR_LEVELING_CONSOLIDATED;
- if (!backing_store_write_bulk(0, (backing_store_int_t *)wear_leveling.cache, sizeof(wear_leveling.cache) / sizeof(backing_store_int_t))) {
- wl_dprintf("Failed to write to backing store\n");
- status = WEAR_LEVELING_FAILED;
- }
- if (status != WEAR_LEVELING_FAILED) {
- // Write out the FNV1a_64 result of the consolidated data
- write_log_entry_t entry;
- entry.raw64 = fnv_64a_buf(wear_leveling.cache, (WEAR_LEVELING_LOGICAL_SIZE), FNV1A_64_INIT);
- wl_dprintf("Writing checksum\n");
- do {
- #if BACKING_STORE_WRITE_SIZE == 2
- if (!backing_store_write_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw16, 4)) {
- status = WEAR_LEVELING_FAILED;
- break;
- }
- #elif BACKING_STORE_WRITE_SIZE == 4
- if (!backing_store_write_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw32, 2)) {
- status = WEAR_LEVELING_FAILED;
- break;
- }
- #elif BACKING_STORE_WRITE_SIZE == 8
- if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE), entry.raw64)) {
- status = WEAR_LEVELING_FAILED;
- break;
- }
- #endif
- } while (0);
- }
- if (lock_status == STATUS_SUCCESS) {
- wear_leveling_lock();
- }
- return status;
- }
- /**
- * Forces a write of the current cache.
- * Erases the backing store, including the write log.
- * During this operation, there is the potential for data loss if a power loss occurs.
- */
- static wear_leveling_status_t wear_leveling_consolidate_force(void) {
- wl_dprintf("Erasing backing store\n");
- // Erase the backing store. Expectation is that any un-written values that are read back after this call come back as zero.
- bool ok = backing_store_erase();
- if (!ok) {
- wl_dprintf("Failed to erase backing store\n");
- return WEAR_LEVELING_FAILED;
- }
- // Write the cache to the first section of the backing store.
- wear_leveling_status_t status = wear_leveling_write_consolidated();
- if (status == WEAR_LEVELING_FAILED) {
- wl_dprintf("Failed to write consolidated data\n");
- }
- // Next write of the log occurs after the consolidated values at the start of the backing store.
- wear_leveling.write_address = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 due to the FNV1a_64 of the consolidated area
- return status;
- }
- /**
- * Potential write of the current cache to the backing store.
- * Skipped if the current write log position is not at the end of the backing store.
- * During this operation, there is the potential for data loss if a power loss occurs.
- *
- * @return true if consolidation occurred
- */
- static wear_leveling_status_t wear_leveling_consolidate_if_needed(void) {
- if (wear_leveling.write_address >= (WEAR_LEVELING_BACKING_SIZE)) {
- return wear_leveling_consolidate_force();
- }
- return WEAR_LEVELING_SUCCESS;
- }
- /**
- * Appends the supplied fixed-width entry to the write log, optionally consolidating if the log is full.
- *
- * @return true if consolidation occurred
- */
- static wear_leveling_status_t wear_leveling_append_raw(backing_store_int_t value) {
- bool ok = backing_store_write(wear_leveling.write_address, value);
- if (!ok) {
- wl_dprintf("Failed to write to backing store\n");
- return WEAR_LEVELING_FAILED;
- }
- wear_leveling.write_address += (BACKING_STORE_WRITE_SIZE);
- return wear_leveling_consolidate_if_needed();
- }
- /**
- * Handles writing multi_byte-encoded data to the backing store.
- *
- * @return true if consolidation occurred
- */
- static wear_leveling_status_t wear_leveling_write_raw_multibyte(uint32_t address, const void *value, size_t length) {
- const uint8_t * p = value;
- write_log_entry_t log = LOG_ENTRY_MAKE_MULTIBYTE(address, length);
- for (size_t i = 0; i < length; ++i) {
- log.raw8[3 + i] = p[i];
- }
- // Write to the backing store. See the multi-byte log format in the documentation header at the top of the file.
- wear_leveling_status_t status;
- #if BACKING_STORE_WRITE_SIZE == 2
- status = wear_leveling_append_raw(log.raw16[0]);
- if (status != WEAR_LEVELING_SUCCESS) {
- return status;
- }
- status = wear_leveling_append_raw(log.raw16[1]);
- if (status != WEAR_LEVELING_SUCCESS) {
- return status;
- }
- if (length > 1) {
- status = wear_leveling_append_raw(log.raw16[2]);
- if (status != WEAR_LEVELING_SUCCESS) {
- return status;
- }
- }
- if (length > 3) {
- status = wear_leveling_append_raw(log.raw16[3]);
- if (status != WEAR_LEVELING_SUCCESS) {
- return status;
- }
- }
- #elif BACKING_STORE_WRITE_SIZE == 4
- status = wear_leveling_append_raw(log.raw32[0]);
- if (status != WEAR_LEVELING_SUCCESS) {
- return status;
- }
- if (length > 1) {
- status = wear_leveling_append_raw(log.raw32[1]);
- if (status != WEAR_LEVELING_SUCCESS) {
- return status;
- }
- }
- #elif BACKING_STORE_WRITE_SIZE == 8
- status = wear_leveling_append_raw(log.raw64);
- if (status != WEAR_LEVELING_SUCCESS) {
- return status;
- }
- #endif
- return status;
- }
- /**
- * Handles the actual writing of logical data into the write log section of the backing store.
- */
- static wear_leveling_status_t wear_leveling_write_raw(uint32_t address, const void *value, size_t length) {
- const uint8_t * p = value;
- size_t remaining = length;
- wear_leveling_status_t status = WEAR_LEVELING_SUCCESS;
- while (remaining > 0) {
- #if BACKING_STORE_WRITE_SIZE == 2
- // Small-write optimizations - uint16_t, 0 or 1, address is even, address <16384:
- if (remaining >= 2 && address % 2 == 0 && address < 16384) {
- const uint16_t v = ((uint16_t)p[1]) << 8 | p[0]; // don't just dereference a uint16_t here -- if unaligned it generates faults on some MCUs
- if (v == 0 || v == 1) {
- const write_log_entry_t log = LOG_ENTRY_MAKE_WORD_01(address, v);
- status = wear_leveling_append_raw(log.raw16[0]);
- if (status != WEAR_LEVELING_SUCCESS) {
- // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue.
- // If a failure occurred, pass it on.
- return status;
- }
- remaining -= 2;
- address += 2;
- p += 2;
- continue;
- }
- }
- // Small-write optimizations - address<64:
- if (address < 64) {
- const write_log_entry_t log = LOG_ENTRY_MAKE_OPTIMIZED_64(address, *p);
- status = wear_leveling_append_raw(log.raw16[0]);
- if (status != WEAR_LEVELING_SUCCESS) {
- // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue.
- // If a failure occurred, pass it on.
- return status;
- }
- remaining--;
- address++;
- p++;
- continue;
- }
- #endif // BACKING_STORE_WRITE_SIZE == 2
- const size_t this_length = remaining >= LOG_ENTRY_MULTIBYTE_MAX_BYTES ? LOG_ENTRY_MULTIBYTE_MAX_BYTES : remaining;
- status = wear_leveling_write_raw_multibyte(address, p, this_length);
- if (status != WEAR_LEVELING_SUCCESS) {
- // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue.
- // If a failure occurred, pass it on.
- return status;
- }
- remaining -= this_length;
- address += (uint32_t)this_length;
- p += this_length;
- }
- return status;
- }
- /**
- * "Replays" the write log from the backing store, updating the local cache with updated values.
- */
- static wear_leveling_status_t wear_leveling_playback_log(void) {
- wl_dprintf("Playback write log\n");
- wear_leveling_status_t status = WEAR_LEVELING_SUCCESS;
- bool cancel_playback = false;
- uint32_t address = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 due to the FNV1a_64 of the consolidated area
- while (!cancel_playback && address < (WEAR_LEVELING_BACKING_SIZE)) {
- backing_store_int_t value;
- bool ok = backing_store_read(address, &value);
- if (!ok) {
- wl_dprintf("Failed to load from backing store, skipping playback of write log\n");
- cancel_playback = true;
- status = WEAR_LEVELING_FAILED;
- break;
- }
- if (value == 0) {
- wl_dprintf("Found empty slot, no more log entries\n");
- cancel_playback = true;
- break;
- }
- // If we got a nonzero value, then we need to increment the address to ensure next write occurs at next location
- address += (BACKING_STORE_WRITE_SIZE);
- // Read from the write log
- write_log_entry_t log;
- #if BACKING_STORE_WRITE_SIZE == 2
- log.raw16[0] = value;
- #elif BACKING_STORE_WRITE_SIZE == 4
- log.raw32[0] = value;
- #elif BACKING_STORE_WRITE_SIZE == 8
- log.raw64 = value;
- #endif
- switch (LOG_ENTRY_GET_TYPE(log)) {
- case LOG_ENTRY_TYPE_MULTIBYTE: {
- #if BACKING_STORE_WRITE_SIZE == 2
- ok = backing_store_read(address, &log.raw16[1]);
- if (!ok) {
- wl_dprintf("Failed to load from backing store, skipping playback of write log\n");
- cancel_playback = true;
- status = WEAR_LEVELING_FAILED;
- break;
- }
- address += (BACKING_STORE_WRITE_SIZE);
- #endif // BACKING_STORE_WRITE_SIZE == 2
- const uint32_t a = LOG_ENTRY_MULTIBYTE_GET_ADDRESS(log);
- const uint8_t l = LOG_ENTRY_MULTIBYTE_GET_LENGTH(log);
- if (a + l > (WEAR_LEVELING_LOGICAL_SIZE)) {
- cancel_playback = true;
- status = WEAR_LEVELING_FAILED;
- break;
- }
- #if BACKING_STORE_WRITE_SIZE == 2
- if (l > 1) {
- ok = backing_store_read(address, &log.raw16[2]);
- if (!ok) {
- wl_dprintf("Failed to load from backing store, skipping playback of write log\n");
- cancel_playback = true;
- status = WEAR_LEVELING_FAILED;
- break;
- }
- address += (BACKING_STORE_WRITE_SIZE);
- }
- if (l > 3) {
- ok = backing_store_read(address, &log.raw16[3]);
- if (!ok) {
- wl_dprintf("Failed to load from backing store, skipping playback of write log\n");
- cancel_playback = true;
- status = WEAR_LEVELING_FAILED;
- break;
- }
- address += (BACKING_STORE_WRITE_SIZE);
- }
- #elif BACKING_STORE_WRITE_SIZE == 4
- if (l > 1) {
- ok = backing_store_read(address, &log.raw32[1]);
- if (!ok) {
- wl_dprintf("Failed to load from backing store, skipping playback of write log\n");
- cancel_playback = true;
- status = WEAR_LEVELING_FAILED;
- break;
- }
- address += (BACKING_STORE_WRITE_SIZE);
- }
- #endif
- memcpy(&wear_leveling.cache[a], &log.raw8[3], l);
- } break;
- #if BACKING_STORE_WRITE_SIZE == 2
- case LOG_ENTRY_TYPE_OPTIMIZED_64: {
- const uint32_t a = LOG_ENTRY_OPTIMIZED_64_GET_ADDRESS(log);
- const uint8_t v = LOG_ENTRY_OPTIMIZED_64_GET_VALUE(log);
- if (a >= (WEAR_LEVELING_LOGICAL_SIZE)) {
- cancel_playback = true;
- status = WEAR_LEVELING_FAILED;
- break;
- }
- wear_leveling.cache[a] = v;
- } break;
- case LOG_ENTRY_TYPE_WORD_01: {
- const uint32_t a = LOG_ENTRY_WORD_01_GET_ADDRESS(log);
- const uint8_t v = LOG_ENTRY_WORD_01_GET_VALUE(log);
- if (a + 1 >= (WEAR_LEVELING_LOGICAL_SIZE)) {
- cancel_playback = true;
- status = WEAR_LEVELING_FAILED;
- break;
- }
- wear_leveling.cache[a + 0] = v;
- wear_leveling.cache[a + 1] = 0;
- } break;
- #endif // BACKING_STORE_WRITE_SIZE == 2
- default: {
- cancel_playback = true;
- status = WEAR_LEVELING_FAILED;
- } break;
- }
- }
- // We've reached the end of the log, so we're at the new write location
- wear_leveling.write_address = address;
- if (status == WEAR_LEVELING_FAILED) {
- // If we had a failure during readback, assume we're corrupted -- force a consolidation with the data we already have
- status = wear_leveling_consolidate_force();
- } else {
- // Consolidate the cache + write log if required
- status = wear_leveling_consolidate_if_needed();
- }
- return status;
- }
- /**
- * Wear-leveling initialization
- */
- wear_leveling_status_t wear_leveling_init(void) {
- wl_dprintf("Init\n");
- // Reset the cache
- wear_leveling_clear_cache();
- // Initialise the backing store
- if (!backing_store_init()) {
- // If it failed, clear the cache and return with failure
- wear_leveling_clear_cache();
- return WEAR_LEVELING_FAILED;
- }
- // Read the previous consolidated values, then replay the existing write log so that the cache has the "live" values
- wear_leveling_status_t status = wear_leveling_read_consolidated();
- if (status == WEAR_LEVELING_FAILED) {
- // If it failed, clear the cache and return with failure
- wear_leveling_clear_cache();
- return status;
- }
- status = wear_leveling_playback_log();
- if (status == WEAR_LEVELING_FAILED) {
- // If it failed, clear the cache and return with failure
- wear_leveling_clear_cache();
- return status;
- }
- return status;
- }
- /**
- * Wear-leveling erase.
- * Post-condition: any reads from the backing store directly after an erase operation must come back as zero.
- */
- wear_leveling_status_t wear_leveling_erase(void) {
- wl_dprintf("Erase\n");
- // Unlock the backing store
- backing_store_lock_status_t lock_status = wear_leveling_unlock();
- if (lock_status == STATUS_FAILURE) {
- wear_leveling_lock();
- return WEAR_LEVELING_FAILED;
- }
- // Perform the erase
- bool ret = backing_store_erase();
- wear_leveling_clear_cache();
- // Lock the backing store if we acquired the lock successfully
- if (lock_status == STATUS_SUCCESS) {
- ret &= (wear_leveling_lock() != STATUS_FAILURE);
- }
- return ret ? WEAR_LEVELING_SUCCESS : WEAR_LEVELING_FAILED;
- }
- /**
- * Writes logical data into the backing store. Skips writes if there are no changes to values.
- */
- wear_leveling_status_t wear_leveling_write(const uint32_t address, const void *value, size_t length) {
- wl_assert(address + length <= (WEAR_LEVELING_LOGICAL_SIZE));
- if (address + length > (WEAR_LEVELING_LOGICAL_SIZE)) {
- return WEAR_LEVELING_FAILED;
- }
- wl_dprintf("Write ");
- wl_dump(address, value, length);
- // Skip write if there's no change compared to the current cached value
- if (memcmp(value, &wear_leveling.cache[address], length) == 0) {
- return true;
- }
- // Update the cache before writing to the backing store -- if we hit the end of the backing store during writes to the log then we'll force a consolidation in-line
- memcpy(&wear_leveling.cache[address], value, length);
- // Unlock the backing store
- backing_store_lock_status_t lock_status = wear_leveling_unlock();
- if (lock_status == STATUS_FAILURE) {
- wear_leveling_lock();
- return WEAR_LEVELING_FAILED;
- }
- // Perform the actual write
- wear_leveling_status_t status = wear_leveling_write_raw(address, value, length);
- switch (status) {
- case WEAR_LEVELING_CONSOLIDATED:
- case WEAR_LEVELING_FAILED:
- // If the write triggered consolidation, or the write failed, then nothing else needs to occur.
- break;
- case WEAR_LEVELING_SUCCESS:
- // Consolidate the cache + write log if required
- status = wear_leveling_consolidate_if_needed();
- break;
- default:
- // Unsure how we'd get here...
- status = WEAR_LEVELING_FAILED;
- break;
- }
- if (lock_status == STATUS_SUCCESS) {
- if (wear_leveling_lock() == STATUS_FAILURE) {
- status = WEAR_LEVELING_FAILED;
- }
- }
- return status;
- }
- /**
- * Reads logical data from the cache.
- */
- wear_leveling_status_t wear_leveling_read(const uint32_t address, void *value, size_t length) {
- wl_assert(address + length <= (WEAR_LEVELING_LOGICAL_SIZE));
- if (address + length > (WEAR_LEVELING_LOGICAL_SIZE)) {
- return WEAR_LEVELING_FAILED;
- }
- // Only need to copy from the cache
- memcpy(value, &wear_leveling.cache[address], length);
- wl_dprintf("Read ");
- wl_dump(address, value, length);
- return WEAR_LEVELING_SUCCESS;
- }
- /**
- * Weak implementation of bulk read, drivers can implement more optimised implementations.
- */
- __attribute__((weak)) bool backing_store_read_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {
- for (size_t i = 0; i < item_count; ++i) {
- if (!backing_store_read(address + (i * BACKING_STORE_WRITE_SIZE), &values[i])) {
- return false;
- }
- }
- return true;
- }
- /**
- * Weak implementation of bulk write, drivers can implement more optimised implementations.
- */
- __attribute__((weak)) bool backing_store_write_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {
- for (size_t i = 0; i < item_count; ++i) {
- if (!backing_store_write(address + (i * BACKING_STORE_WRITE_SIZE), values[i])) {
- return false;
- }
- }
- return true;
- }
|