int state_manager_store_non_volatile_state()

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, &sector_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;
}