rusefi-1/firmware/controllers/flash_main.cpp

202 lines
6.1 KiB
C++

/**
* @file flash_main.cpp
* @brief Higher-level logic of saving data into internal flash memory
*
*
* @date Sep 19, 2013
* @author Andrey Belomutskiy, (c) 2012-2020
*/
#include "global.h"
#if EFI_INTERNAL_FLASH
#include "os_access.h"
#include "flash_main.h"
#include "eficonsole.h"
#include "flash_int.h"
#include "engine_math.h"
#if EFI_TUNER_STUDIO
#include "tunerstudio.h"
#endif
#include "engine_controller.h"
static bool needToWriteConfiguration = false;
EXTERN_ENGINE;
static Logging* logger;
extern persistent_config_container_s persistentState;
extern engine_configuration_s *engineConfiguration;
/**
* https://sourceforge.net/p/rusefi/tickets/335/
*
* In order to preserve at least one copy of the tune in case of electrical issues address of second configuration copy
* should be in a different sector of flash since complete flash sectors are erased on write.
*/
crc_t flashStateCrc(persistent_config_container_s *state) {
return calc_crc((const crc_t*) &state->persistentConfiguration, sizeof(persistent_config_s));
}
void setNeedToWriteConfiguration(void) {
scheduleMsg(logger, "Scheduling configuration write");
needToWriteConfiguration = true;
}
bool getNeedToWriteConfiguration(void) {
return needToWriteConfiguration;
}
void writeToFlashIfPending() {
if (!getNeedToWriteConfiguration()) {
return;
}
// todo: technically we need a lock here, realistically we should be fine.
needToWriteConfiguration = false;
scheduleMsg(logger, "Writing pending configuration");
writeToFlashNow();
}
// Erase and write a copy of the configuration at the specified address
template <typename TStorage>
int eraseAndFlashCopy(flashaddr_t storageAddress, const TStorage& data)
{
intFlashErase(storageAddress, sizeof(TStorage));
return intFlashWrite(storageAddress, reinterpret_cast<const char*>(&data), sizeof(TStorage));
}
void writeToFlashNow(void) {
scheduleMsg(logger, " !!!!!!!!!!!!!!!!!!!! BE SURE NOT WRITE WITH IGNITION ON !!!!!!!!!!!!!!!!!!!!");
// Set up the container
persistentState.size = sizeof(persistentState);
persistentState.version = FLASH_DATA_VERSION;
persistentState.value = flashStateCrc(&persistentState);
// Flash two copies
int result1 = eraseAndFlashCopy(getFlashAddrFirstCopy(), persistentState);
int result2 = eraseAndFlashCopy(getFlashAddrSecondCopy(), persistentState);
// handle success/failure
bool isSuccess = (result1 == FLASH_RETURN_SUCCESS) && (result2 == FLASH_RETURN_SUCCESS);
if (isSuccess) {
scheduleMsg(logger, "FLASH_SUCCESS");
} else {
scheduleMsg(logger, "Flashing failed");
}
assertEngineReference();
#if EFI_SHAFT_POSITION_INPUT
resetMaxValues();
#endif
}
static bool isValidCrc(persistent_config_container_s *state) {
crc_t result = flashStateCrc(state);
int isValidCrc_b = result == state->value;
return isValidCrc_b;
}
static void doResetConfiguration(void) {
resetConfigurationExt(logger, engineConfiguration->engineType PASS_ENGINE_PARAMETER_SUFFIX);
}
persisted_configuration_state_e flashState;
static persisted_configuration_state_e doReadConfiguration(flashaddr_t address, Logging * logger) {
printMsg(logger, "readFromFlash %x", address);
intFlashRead(address, (char *) &persistentState, sizeof(persistentState));
if (!isValidCrc(&persistentState)) {
return CRC_FAILED;
} else if (persistentState.version != FLASH_DATA_VERSION || persistentState.size != sizeof(persistentState)) {
return INCOMPATIBLE_VERSION;
} else {
return PC_OK;
}
}
/**
* this method could and should be executed before we have any
* connectivity so no console output here
*/
persisted_configuration_state_e readConfiguration(Logging * logger) {
efiAssert(CUSTOM_ERR_ASSERT, getCurrentRemainingStack() > EXPECTED_REMAINING_STACK, "read f", PC_ERROR);
persisted_configuration_state_e result = doReadConfiguration(getFlashAddrFirstCopy(), logger);
if (result != PC_OK) {
printMsg(logger, "Reading second configuration copy");
result = doReadConfiguration(getFlashAddrSecondCopy(), logger);
}
if (result == CRC_FAILED) {
// we are here on first boot on brand new chip
warning(CUSTOM_ERR_FLASH_CRC_FAILED, "flash CRC failed");
resetConfigurationExt(logger, DEFAULT_ENGINE_TYPE PASS_ENGINE_PARAMETER_SUFFIX);
} else if (result == INCOMPATIBLE_VERSION) {
resetConfigurationExt(logger, engineConfiguration->engineType PASS_ENGINE_PARAMETER_SUFFIX);
} else {
/**
* At this point we know that CRC and version number is what we expect. Safe to assume it's a valid configuration.
*/
applyNonPersistentConfiguration(logger PASS_ENGINE_PARAMETER_SUFFIX);
}
// we can only change the state after the CRC check
engineConfiguration->byFirmwareVersion = getRusEfiVersion();
memset(persistentState.persistentConfiguration.warning_message , 0, ERROR_BUFFER_SIZE);
validateConfiguration(PASS_ENGINE_PARAMETER_SIGNATURE);
return result;
}
void readFromFlash(void) {
persisted_configuration_state_e result = readConfiguration(logger);
if (result == CRC_FAILED) {
printMsg(logger, "Need to reset flash to default due to CRC");
} else if (result == INCOMPATIBLE_VERSION) {
printMsg(logger, "Resetting but saving engine type [%d]", engineConfiguration->engineType);
} else {
printMsg(logger, "Got valid configuration from flash!");
}
}
static void rewriteConfig(void) {
doResetConfiguration();
writeToFlashNow();
}
static void writeConfigCommand() {
#if EFI_TUNER_STUDIO
// on start-up rusEfi would read from working copy of TS while
// we have a lot of console commands which write into real copy of configuration directly
// we have a bit of a mess here
syncTunerStudioCopy();
#endif /* EFI_TUNER_STUDIO */
writeToFlashNow();
}
void initFlash(Logging *sharedLogger) {
logger = sharedLogger;
addConsoleAction("readconfig", readFromFlash);
/**
* This would write NOW (you should not be doing this while connected to real engine)
*/
addConsoleAction(CMD_WRITECONFIG, writeConfigCommand);
#if EFI_TUNER_STUDIO
/**
* This would schedule write to flash once the engine is stopped
*/
addConsoleAction("burnconfig", requestBurn);
#endif
addConsoleAction("resetconfig", doResetConfiguration);
addConsoleAction("rewriteconfig", rewriteConfig);
}
#endif /* EFI_INTERNAL_FLASH */