in core/state_manager/state_manager.c [360:503]
int state_manager_store_non_volatile_state (struct state_manager *manager)
{
int status = 0;
int erase_status;
uint16_t store_state;
uint16_t nv_state[4];
uint32_t next_addr;
uint32_t sector_size;
uint16_t in_flash;
bool bit_error = false;
bool refresh = false;
if (manager == NULL) {
return STATE_MANAGER_INVALID_ARGUMENT;
}
status = manager->nv_store->get_sector_size (manager->nv_store, §or_size);
if (status != 0) {
return status;
}
platform_mutex_lock (&manager->store_lock);
platform_mutex_lock (&manager->state_lock);
store_state = manager->nv_state & ~SINGLE_BYTE_STATE;
store_state |= MULTI_BYTE_STATE;
platform_mutex_unlock (&manager->state_lock);
/* If our current state hasn't changed from what is on flash, verify the flash contents and
* refresh as necessary. */
if (store_state == manager->last_nv_stored) {
status = manager->nv_store->read (manager->nv_store, manager->store_addr,
(uint8_t*) nv_state, sizeof (nv_state));
if (status == 0) {
in_flash = state_manager_read_state_bits (nv_state, &bit_error, &refresh);
if ((in_flash != store_state) || bit_error) {
/* The data in flash is bad, so force the state to be rewritten. */
manager->last_nv_stored = 0xffff;
if (refresh) {
state_manager_set_next_sector_write_offset (manager, sector_size);
}
}
}
}
/* If our current state is different from that stored on flash, write it to flash. */
if (store_state != manager->last_nv_stored) {
next_addr = manager->store_addr + 8;
if (next_addr == (manager->base_addr + (sector_size * 2))) {
next_addr = manager->base_addr;
}
nv_state[0] = store_state;
nv_state[1] = store_state;
nv_state[2] = store_state;
nv_state[3] = 0;
/* Make sure we are writing to a blank sector.
*
* If we are trying to write the last entry in the current sector, make sure the next sector
* is erased. Otherwise, we will not be able to correctly determine the last state stored
* by looking for blank flash during initialization. */
if ((next_addr == manager->base_addr) && !(manager->volatile_state & SECTOR_1_BLANK)) {
status = STATE_MANAGER_NOT_BLANK;
}
else if ((next_addr == (manager->base_addr + sector_size)) &&
!(manager->volatile_state & SECTOR_2_BLANK)) {
status = STATE_MANAGER_NOT_BLANK;
}
else if ((next_addr == (manager->base_addr + (sector_size * 2) - 8)) &&
!(manager->volatile_state & SECTOR_1_BLANK)) {
status = STATE_MANAGER_NOT_BLANK;
}
else if ((next_addr == (manager->base_addr + sector_size - 8)) &&
!(manager->volatile_state & SECTOR_2_BLANK)) {
status = STATE_MANAGER_NOT_BLANK;
}
if (status == 0) {
status = manager->nv_store->write (manager->nv_store, next_addr, (uint8_t*) nv_state,
sizeof (nv_state));
if (ROT_IS_ERROR (status)) {
platform_mutex_unlock (&manager->store_lock);
return status;
}
if (status == sizeof (nv_state)) {
status = 0;
manager->last_nv_stored = store_state;
}
else {
/* We handle this scenario, but only minimally. This is not really possible given
* the alignment of data. */
status = STATE_MANAGER_INCOMPLETE_WRITE;
}
manager->store_addr = next_addr;
}
}
/* Always make sure the unused sector is erased so it is ready to be written to when needed.
* A failure to erase is not a reported error since the data was successfully stored. */
if (manager->store_addr < (manager->base_addr + sector_size)) {
if (manager->volatile_state & SECTOR_1_BLANK) {
manager->volatile_state &= ~SECTOR_1_BLANK;
}
if (!(manager->volatile_state & SECTOR_2_BLANK)) {
erase_status = flash_sector_erase_region_and_verify (manager->nv_store,
manager->base_addr + sector_size, sector_size);
if (erase_status == 0) {
manager->volatile_state |= SECTOR_2_BLANK;
}
else {
debug_log_create_entry (DEBUG_LOG_SEVERITY_WARNING, DEBUG_LOG_COMPONENT_STATE_MGR,
STATE_LOGGING_ERASE_FAIL, manager->base_addr + sector_size, status);
}
}
}
else {
if (manager->volatile_state & SECTOR_2_BLANK) {
manager->volatile_state &= ~SECTOR_2_BLANK;
}
if (!(manager->volatile_state & SECTOR_1_BLANK)) {
erase_status = flash_sector_erase_region_and_verify (manager->nv_store,
manager->base_addr, sector_size);
if (erase_status == 0) {
manager->volatile_state |= SECTOR_1_BLANK;
}
else {
debug_log_create_entry (DEBUG_LOG_SEVERITY_WARNING, DEBUG_LOG_COMPONENT_STATE_MGR,
STATE_LOGGING_ERASE_FAIL, manager->base_addr, status);
}
}
}
platform_mutex_unlock (&manager->store_lock);
return status;
}