20#include "freertos/FreeRTOS.h"
21#include "freertos/task.h"
22#include "freertos/semphr.h"
30#include "esp_app_desc.h"
32#include "esp_ota_ops.h"
33#include "esp_system.h"
37static const char *
const TAG =
"ota";
45#define OTA_MIN_IMAGE (256U * 1024U)
57static SemaphoreHandle_t
s_lock = NULL;
66static const esp_partition_t *
s_dst = NULL;
71 if (
s_lock == NULL) {
s_lock = xSemaphoreCreateMutex(); }
87static bool refuse(
const char **err,
const char *msg)
90 ESP_LOGW(
TAG,
"upload refused: %s", msg);
96 static const char *ignored =
"";
97 if (err == NULL) { err = &ignored; }
100 return refuse(err,
"The terminal is out of memory.");
103 return refuse(err,
"Another upload is in progress.");
107 if (xSemaphoreTake(
s_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
109 (void)xSemaphoreGive(
s_lock);
112 return refuse(err,
"An update is already waiting to be accepted on the "
113 "terminal screen. Accept or discard it there first.");
116 s_dst = esp_ota_get_next_update_partition(NULL);
120 ESP_LOGE(
TAG,
"no OTA slot - unit needs a serial reflash first");
121 return refuse(err,
"This terminal has no second firmware slot. It has to "
122 "be reflashed over USB once before it can take "
126 return refuse(err,
"That file is too small to be firmware.");
128 if (len >
s_dst->size) {
129 return refuse(err,
"That file is larger than the firmware slot.");
135 ESP_LOGE(
TAG,
"esp_ota_begin: %s", esp_err_to_name(rc));
136 return refuse(err,
"The terminal could not prepare its firmware slot.");
140 ESP_LOGI(
TAG,
"receiving %u bytes into '%s'",
141 static_cast<unsigned>(len),
s_dst->label);
148 const esp_err_t rc = esp_ota_write(
s_handle, buf, n);
150 ESP_LOGE(
TAG,
"esp_ota_write: %s", esp_err_to_name(rc));
162 ESP_LOGW(
TAG,
"upload aborted - nothing installed");
167bool ota_end(
char *ver_out,
size_t ver_n,
const char **err)
169 static const char *ignored =
"";
170 if (err == NULL) { err = &ignored; }
173 *err =
"No upload was in progress.";
177 const esp_err_t rc = esp_ota_end(
s_handle);
181 ESP_LOGE(
TAG,
"image rejected: %s", esp_err_to_name(rc));
182 *err = (rc == ESP_ERR_OTA_VALIDATE_FAILED)
183 ?
"The terminal rejected that image: it is not valid firmware, or "
184 "it is not signed with the key this terminal trusts."
185 :
"The terminal could not store that image.";
192 memset(&desc, 0,
sizeof(desc));
194 if ((
s_dst != NULL) &&
195 (esp_ota_get_partition_description(
s_dst, &desc) == ESP_OK)) {
196 (void)snprintf(ver,
sizeof(ver),
"%.*s",
197 static_cast<int>(
sizeof(desc.version)), desc.version);
200 if (xSemaphoreTake(
s_lock, pdMS_TO_TICKS(1000)) != pdTRUE) {
201 *err =
"The terminal is busy.";
207 (void)xSemaphoreGive(
s_lock);
209 ESP_LOGW(
TAG,
"staged %s in '%s' - awaiting on-screen accept", ver,
211 if ((ver_out != NULL) && (ver_n > 0U)) {
212 (void)snprintf(ver_out, ver_n,
"%s", ver);
223 const esp_partition_t *idle = esp_ota_get_next_update_partition(NULL);
224 if (idle == NULL) {
return false; }
226 esp_ota_img_states_t st = ESP_OTA_IMG_UNDEFINED;
227 if (esp_ota_get_state_partition(idle, &st) != ESP_OK) {
return false; }
228 return (st == ESP_OTA_IMG_ABORTED) || (st == ESP_OTA_IMG_INVALID);
237 ESP_LOGE(
TAG,
"the last update did NOT stick: the new image booted and "
238 "never confirmed itself, so this terminal rolled back to %s. "
239 "Install it again and leave it alone until the reader is up.",
243 const esp_partition_t *run = esp_ota_get_running_partition();
244 if (run == NULL) {
return false; }
246 esp_ota_img_states_t st = ESP_OTA_IMG_UNDEFINED;
247 if (esp_ota_get_state_partition(run, &st) != ESP_OK) {
return false; }
248 if (st != ESP_OTA_IMG_PENDING_VERIFY) {
return false; }
250 if (esp_ota_mark_app_valid_cancel_rollback() == ESP_OK) {
251 ESP_LOGW(
TAG,
"update to %s confirmed - rollback cancelled",
256 ESP_LOGE(
TAG,
"could not confirm this image - it WILL roll back");
266 const esp_app_desc_t *d = esp_app_get_description();
270 static_cast<int>(
sizeof(d->version)), d->version);
277 if (
s_lock == NULL) {
return false; }
278 if (xSemaphoreTake(
s_lock, pdMS_TO_TICKS(100)) != pdTRUE) {
return false; }
282 if ((version != NULL) && (version_n > 0U)) {
287 (void)xSemaphoreGive(
s_lock);
293 if (
s_lock == NULL) {
return false; }
294 if (xSemaphoreTake(
s_lock, pdMS_TO_TICKS(100)) != pdTRUE) {
return false; }
301 (void)xSemaphoreGive(
s_lock);
303 if (!staged || !install) {
return false; }
307 const esp_partition_t *dst = esp_ota_get_next_update_partition(NULL);
308 const esp_err_t rc = esp_ota_set_boot_partition(dst);
310 ESP_LOGE(
TAG,
"esp_ota_set_boot_partition: %s", esp_err_to_name(rc));
314 ESP_LOGW(
TAG,
"installing %s from '%s' - rebooting", ver,
315 (dst != NULL) ? dst->label :
"?");
317 vTaskDelay(pdMS_TO_TICKS(500));
static const char *const TAG
void ota_abort(void)
Give up on an upload in progress. Nothing is installed. Safe always.
const char * ota_running_version(void)
The running firmware's version, from the image header.
bool ota_end(char *ver_out, size_t ver_n, const char **err)
Close and verify the received image, then stage it for the panel.
bool ota_mark_valid(void)
Confirm the running image, cancelling the rollback armed by the bootloader.
bool ota_receiving(void)
Whether an upload is in flight, so a second can be refused.
static char s_staged_ver[OTA_VERSION_MAX+1]
static esp_ota_handle_t s_handle
static SemaphoreHandle_t s_lock
static bool s_staged_older
static const esp_partition_t * s_dst
static bool lock_ready(void)
Create the staging lock on first use.
static bool refuse(const char **err, const char *msg)
Refuse an upload, saying so to the browser AND to the log.
bool ota_staged(char *version, size_t version_n, bool *older)
Fetch the version of an image that has been received and verified but not yet installed.
static volatile bool s_receiving
bool ota_last_update_failed(void)
Whether the last update was installed and then thrown away.
bool ota_write(const void *buf, size_t n)
Append n bytes to the open slot.
bool ota_commit(bool install)
Resolve a staged image.
static char s_running_ver[OTA_VERSION_MAX+1]
bool ota_begin(size_t len, const char **err)
Open the idle slot for an image of len bytes.
Firmware slot handling: receive an image into the idle slot, verify it, and install it only once some...
Dotted version comparison, for deciding whether an update goes forwards or backwards.
#define OTA_VERSION_MAX
Longest version string looked at, matching esp_app_desc_t::version.
static int ota_version_cmp(const char *a, const char *b)
Order two version strings.