fix(ota): Correctly select the partition to write to

There was an off-by-one error selecting the new partition, meaning the OTA
update would write to the second partition but the boot would be set to the same
one we're currently in.

I believe this should fix jpirnay/crosspoint-reader#112
This commit is contained in:
Joel Goguen
2026-04-28 21:26:52 -04:00
parent 93f5bdb492
commit d148f7290d
2 changed files with 35 additions and 8 deletions
+31 -8
View File
@@ -263,20 +263,30 @@ OtaUpdater::OtaUpdaterError OtaUpdater::beginInstallUpdate() {
int OtaUpdater::forceSetOtaBootPartition() {
const esp_partition_t* newPartition = esp_ota_get_next_update_partition(nullptr);
if (newPartition == nullptr) {
LOG_ERR("OTA", "force boot partition: next update partition not found");
return ESP_ERR_NOT_FOUND;
}
LOG_INF("OTA", "force boot next partition=%s subtype=0x%x offset=0x%lx size=0x%lx", newPartition->label,
newPartition->subtype, static_cast<unsigned long>(newPartition->address),
static_cast<unsigned long>(newPartition->size));
const esp_partition_t* otaDataPartition =
esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, nullptr);
if (otaDataPartition == nullptr) {
LOG_ERR("OTA", "force boot partition: otadata partition not found");
return ESP_ERR_NOT_FOUND;
}
esp_ota_select_entry_t otadata[2];
esp_err_t err = esp_partition_read(otaDataPartition, 0, &otadata[0], sizeof(esp_ota_select_entry_t));
if (err != ESP_OK) return err;
if (err != ESP_OK) {
LOG_ERR("OTA", "force boot: read otadata[0] failed: %s", esp_err_to_name(err));
return err;
}
err = esp_partition_read(otaDataPartition, otaDataPartition->erase_size, &otadata[1], sizeof(esp_ota_select_entry_t));
if (err != ESP_OK) return err;
if (err != ESP_OK) {
LOG_ERR("OTA", "force boot: read otadata[1] failed: %s", esp_err_to_name(err));
return err;
}
int activeSlot = bootloader_common_get_active_otadata(otadata);
int nextSlot = (activeSlot == -1) ? 0 : (~activeSlot & 1);
@@ -287,7 +297,10 @@ int OtaUpdater::forceSetOtaBootPartition() {
nullptr) != nullptr) {
otaAppCount++;
}
if (otaAppCount == 0) return ESP_ERR_NOT_FOUND;
if (otaAppCount == 0) {
LOG_ERR("OTA", "force boot select: no OTA app partitions found");
return ESP_ERR_NOT_FOUND;
}
const uint8_t subTypeId = newPartition->subtype & 0x0F;
uint32_t newSeq;
@@ -296,7 +309,10 @@ int OtaUpdater::forceSetOtaBootPartition() {
} else {
uint32_t currentSeq = otadata[activeSlot].ota_seq;
newSeq = currentSeq;
while (newSeq % otaAppCount != static_cast<uint32_t>(subTypeId)) {
// ESP-IDF's bootloader maps ota_seq to an OTA app slot with
// (ota_seq - 1) % ota_app_count. Match that mapping here so the forced
// otadata entry selects the partition that esp_https_ota just wrote.
while ((newSeq - 1) % otaAppCount != static_cast<uint32_t>(subTypeId)) {
newSeq++;
}
if (newSeq == currentSeq) newSeq += otaAppCount;
@@ -308,10 +324,17 @@ int OtaUpdater::forceSetOtaBootPartition() {
err = esp_partition_erase_range(otaDataPartition, otaDataPartition->erase_size * static_cast<uint32_t>(nextSlot),
otaDataPartition->erase_size);
if (err != ESP_OK) return err;
if (err != ESP_OK) {
LOG_ERR("OTA", "force boot: erase otadata[%d] failed: %s", nextSlot, esp_err_to_name(err));
return err;
}
return esp_partition_write(otaDataPartition, otaDataPartition->erase_size * static_cast<uint32_t>(nextSlot),
&otadata[nextSlot], sizeof(esp_ota_select_entry_t));
err = esp_partition_write(otaDataPartition, otaDataPartition->erase_size * static_cast<uint32_t>(nextSlot),
&otadata[nextSlot], sizeof(esp_ota_select_entry_t));
if (err != ESP_OK) {
LOG_ERR("OTA", "force boot: write otadata[%d] failed: %s", nextSlot, esp_err_to_name(err));
}
return err;
}
OtaUpdater::OtaUpdaterError OtaUpdater::performInstallUpdateStep() {