Files
squeezelite-esp32/components/tools/bootstate.cpp
2025-03-18 17:38:34 -04:00

143 lines
5.7 KiB
C++

#include "bootstate.h"
#include "Config.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_spi_flash.h"
#include "messaging.h"
#include "tools.h"
static const char* TAG = "bootstate";
RTC_NOINIT_ATTR uint32_t RebootCounter;
RTC_NOINIT_ATTR uint32_t RecoveryRebootCounter;
RTC_NOINIT_ATTR uint16_t ColdBootIndicatorFlag;
EXT_RAM_ATTR bool is_recovery_running = false;
EXT_RAM_ATTR bool cold_boot = true;
EXT_RAM_ATTR esp_reset_reason_t xReason = ESP_RST_UNKNOWN;
EXT_RAM_ATTR static bool restarting = false;
uint32_t bootstate_read_counter(void) { return RebootCounter; }
uint32_t bootstate_uptate_counter(int32_t xValue) {
if (RebootCounter > 100) {
RebootCounter = 0;
RecoveryRebootCounter = 0;
}
RebootCounter = (xValue != 0) ? (RebootCounter + xValue) : 0;
RecoveryRebootCounter = (xValue != 0) && is_recovery_running ? (RecoveryRebootCounter + xValue) : 0;
return RebootCounter;
}
void bootstate_handle_boot() {
if (ColdBootIndicatorFlag != 0xFACE) {
ESP_LOGI(TAG, "System is booting from power on.");
cold_boot = true;
ColdBootIndicatorFlag = 0xFACE;
} else {
cold_boot = false;
}
const esp_partition_t* running = esp_ota_get_running_partition();
xReason = esp_reset_reason();
ESP_LOGI(TAG, "Reset reason is: %u. Running from partition %s type %s ", xReason, running->label,
running->subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY ? "Factory" : "Application");
is_recovery_running = (running->subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY);
if (!is_recovery_running) {
/* unscheduled restart (HW, Watchdog or similar) thus increment dynamic
* counter then log current boot statistics as a warning */
uint32_t Counter = bootstate_uptate_counter(1); // increment counter
ESP_LOGI(TAG, "Reboot counter=%u\n", Counter);
if (Counter == 5) {
guided_factory();
}
} else {
uint32_t Counter = bootstate_uptate_counter(1); // increment counter
if (RecoveryRebootCounter == 1 && Counter >= 5) {
// First time we are rebooting in recovery after crashing
messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM,
"System was forced into recovery mode after crash likely caused by some bad "
"configuration\n");
}
ESP_LOGI(TAG, "Recovery Reboot counter=%u\n", Counter);
if (RecoveryRebootCounter == 5) {
ESP_LOGW(TAG, "System rebooted too many times. This could be an indication that "
"configuration is corrupted. Erasing config.");
if (config_erase_config()) {
config_raise_changed(true);
guided_factory();
} else {
ESP_LOGE(TAG, "Error erasing configuration");
}
}
if (RecoveryRebootCounter > 5) {
messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM,
"System was forced into recovery mode after crash likely caused by some bad "
"configuration. Configuration was reset to factory.\n");
}
}
}
esp_err_t guided_boot(esp_partition_subtype_t partition_subtype) {
if (is_recovery_running) {
if (partition_subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY) {
simple_restart();
}
} else {
if (partition_subtype != ESP_PARTITION_SUBTYPE_APP_FACTORY) {
simple_restart();
}
}
esp_err_t err = ESP_OK;
const esp_partition_t* partition;
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, partition_subtype, NULL);
if (it == NULL) {
log_send_messaging(MESSAGING_ERROR, "Reboot failed. Partitions error");
} else {
ESP_LOGD(TAG, "Found partition. Getting info.");
partition = (esp_partition_t*)esp_partition_get(it);
ESP_LOGD(TAG, "Releasing partition iterator");
esp_partition_iterator_release(it);
if (partition != NULL) {
log_send_messaging(MESSAGING_INFO, "Rebooting to %s", partition->label);
err = esp_ota_set_boot_partition(partition);
if (err != ESP_OK) {
log_send_messaging(MESSAGING_ERROR, "Unable to select partition for reboot: %s", esp_err_to_name(err));
}
} else {
log_send_messaging(MESSAGING_ERROR, "partition type %u not found! Unable to reboot to recovery.", partition_subtype);
}
ESP_LOGD(TAG, "Yielding to other processes");
taskYIELD();
simple_restart();
}
return ESP_OK;
}
esp_err_t guided_restart_ota() {
log_send_messaging(MESSAGING_WARNING, "Booting to Squeezelite");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
esp_err_t guided_factory() {
log_send_messaging(MESSAGING_WARNING, "Booting to recovery");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
void simple_restart() {
restarting = true;
log_send_messaging(MESSAGING_WARNING, "Rebooting.");
TimerHandle_t timer = xTimerCreate("reboot", 1, pdFALSE, nullptr, [](TimerHandle_t xTimer) {
if (!config_waitcommit()) {
log_send_messaging(MESSAGING_WARNING, "Waiting for configuration to commit ");
ESP_LOGD(TAG,"Queuing restart asynchronously to ensure all events are flushed.");
network_async_reboot(RESTART);
return;
}
vTaskDelay(750 / portTICK_PERIOD_MS);
esp_restart();
xTimerDelete(xTimer, portMAX_DELAY);
});
xTimerStart(timer, portMAX_DELAY);
}
bool is_restarting() { return restarting; }