mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-10 12:53:29 +00:00
feat(esp_https_ota): added ota resumption feature
This commit enabled configurable OTA resumption feature in advanced ota example. This resumes downloading OTA image from where it left off in case of an error or reboot. Closes https://github.com/espressif/esp-idf/issues/13127
This commit is contained in:

committed by
Mahavir Jain

parent
706b5e44d3
commit
5c5df89950
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <errno.h>
|
||||
#include <sys/param.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_check.h"
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(ESP_HTTPS_OTA_EVENT);
|
||||
|
||||
@@ -35,6 +36,7 @@ typedef enum {
|
||||
ESP_HTTPS_OTA_BEGIN,
|
||||
ESP_HTTPS_OTA_IN_PROGRESS,
|
||||
ESP_HTTPS_OTA_SUCCESS,
|
||||
ESP_HTTPS_OTA_RESUME,
|
||||
} esp_https_ota_state;
|
||||
|
||||
struct esp_https_ota_handle {
|
||||
@@ -292,6 +294,16 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
if (ota_config->decrypt_cb == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (ota_config->ota_resumption) {
|
||||
// OTA resumption is not supported for pre-encrypted firmware case
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_https_ota_t *https_ota_handle = calloc(1, sizeof(esp_https_ota_t));
|
||||
if (!https_ota_handle) {
|
||||
ESP_LOGE(TAG, "Couldn't allocate memory to upgrade data buffer");
|
||||
@@ -309,6 +321,14 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
|
||||
https_ota_handle->max_authorization_retries = 0;
|
||||
}
|
||||
|
||||
if (ota_config->ota_resumption) {
|
||||
// We allow resumption only if we have minimum buffer size already written to flash
|
||||
if (ota_config->ota_image_bytes_written >= DEFAULT_OTA_BUF_SIZE) {
|
||||
ESP_LOGI(TAG, "Valid OTA resumption case, offset %d", ota_config->ota_image_bytes_written);
|
||||
https_ota_handle->binary_file_len = ota_config->ota_image_bytes_written;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initiate HTTP Connection */
|
||||
https_ota_handle->http_client = esp_http_client_init(ota_config->http_config);
|
||||
if (https_ota_handle->http_client == NULL) {
|
||||
@@ -325,6 +345,19 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If OTA resumption is enabled, set the "Range" header to resume downloading the OTA image
|
||||
* from the last written byte. For non-partial cases, the range pattern is 'from-'.
|
||||
* Partial cases ('from-to') are handled separately below based on the remaining data to
|
||||
* be downloaded and the max_http_request_size.
|
||||
*/
|
||||
if (https_ota_handle->binary_file_len > 0 && !https_ota_handle->partial_http_download) {
|
||||
char *header_val = NULL;
|
||||
asprintf(&header_val, "bytes=%d-", https_ota_handle->binary_file_len);
|
||||
esp_http_client_set_header(https_ota_handle->http_client, "Range", header_val);
|
||||
free(header_val);
|
||||
}
|
||||
|
||||
if (https_ota_handle->partial_http_download) {
|
||||
esp_http_client_set_method(https_ota_handle->http_client, HTTP_METHOD_HEAD);
|
||||
err = esp_http_client_perform(https_ota_handle->http_client);
|
||||
@@ -343,14 +376,20 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
|
||||
https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client);
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
/* In case of pre ecnrypted OTA, actual image size of OTA binary includes header size
|
||||
which stored in variable enc_img_header_size*/
|
||||
* which stored in variable enc_img_header_size */
|
||||
https_ota_handle->image_length -= ota_config->enc_img_header_size;
|
||||
#endif
|
||||
esp_http_client_close(https_ota_handle->http_client);
|
||||
|
||||
if (https_ota_handle->image_length > https_ota_handle->max_http_request_size) {
|
||||
const int range_start = https_ota_handle->binary_file_len;
|
||||
const int range_end = (https_ota_handle->image_length > https_ota_handle->max_http_request_size + range_start) ?
|
||||
(range_start + https_ota_handle->max_http_request_size - 1) :
|
||||
https_ota_handle->image_length;
|
||||
|
||||
// Additional sanity to not set Range header if it covers whole image range
|
||||
if ((range_end - range_start) < https_ota_handle->image_length) {
|
||||
char *header_val = NULL;
|
||||
asprintf(&header_val, "bytes=0-%d", https_ota_handle->max_http_request_size - 1);
|
||||
asprintf(&header_val, "bytes=%d-%d", range_start, range_end);
|
||||
if (header_val == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for HTTP header");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
@@ -423,19 +462,14 @@ esp_err_t esp_https_ota_begin(const esp_https_ota_config_t *ota_config, esp_http
|
||||
goto http_cleanup;
|
||||
}
|
||||
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
|
||||
if (ota_config->decrypt_cb == NULL) {
|
||||
err = ESP_ERR_INVALID_ARG;
|
||||
goto http_cleanup;
|
||||
}
|
||||
https_ota_handle->decrypt_cb = ota_config->decrypt_cb;
|
||||
https_ota_handle->decrypt_user_ctx = ota_config->decrypt_user_ctx;
|
||||
https_ota_handle->enc_img_header_size = ota_config->enc_img_header_size;
|
||||
#endif
|
||||
https_ota_handle->ota_upgrade_buf_size = alloc_size;
|
||||
https_ota_handle->bulk_flash_erase = ota_config->bulk_flash_erase;
|
||||
https_ota_handle->binary_file_len = 0;
|
||||
*handle = (esp_https_ota_handle_t)https_ota_handle;
|
||||
https_ota_handle->state = ESP_HTTPS_OTA_BEGIN;
|
||||
https_ota_handle->state = https_ota_handle->binary_file_len ? ESP_HTTPS_OTA_RESUME : ESP_HTTPS_OTA_BEGIN;
|
||||
return ESP_OK;
|
||||
|
||||
http_cleanup:
|
||||
@@ -500,29 +534,43 @@ static esp_err_t get_description_from_image(esp_https_ota_handle_t https_ota_han
|
||||
ESP_LOGE(TAG, "esp_https_ota_get_img_desc: Invalid state");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (read_header(handle) != ESP_OK) {
|
||||
|
||||
unsigned img_info_len = 0;
|
||||
if (handle->partition.final->type == ESP_PARTITION_TYPE_APP) {
|
||||
img_info_len = sizeof(esp_app_desc_t);
|
||||
} else if (handle->partition.final->type == ESP_PARTITION_TYPE_BOOTLOADER) {
|
||||
img_info_len = sizeof(esp_bootloader_desc_t);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "This partition type (%d) is not supported", handle->partition.final->type);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
void *img_info = (void *)&handle->ota_upgrade_buf[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)];
|
||||
unsigned img_info_len;
|
||||
const int offset = sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t);
|
||||
void *img_info = NULL;
|
||||
|
||||
if (handle->binary_file_len >= offset + img_info_len) {
|
||||
esp_err_t ret = esp_partition_read(handle->partition.staging, offset, handle->ota_upgrade_buf, img_info_len);
|
||||
ESP_RETURN_ON_ERROR(ret, TAG, "partition read failed %d", ret);
|
||||
img_info = (void *) handle->ota_upgrade_buf;
|
||||
} else {
|
||||
if (read_header(handle) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
img_info = (void *)&handle->ota_upgrade_buf[offset];
|
||||
}
|
||||
|
||||
if (handle->partition.final->type == ESP_PARTITION_TYPE_APP) {
|
||||
img_info_len = sizeof(esp_app_desc_t);
|
||||
esp_app_desc_t *app_info = (esp_app_desc_t *)img_info;
|
||||
if (app_info->magic_word != ESP_APP_DESC_MAGIC_WORD) {
|
||||
ESP_LOGE(TAG, "Incorrect app descriptor magic");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
} else if (handle->partition.final->type == ESP_PARTITION_TYPE_BOOTLOADER) {
|
||||
img_info_len = sizeof(esp_bootloader_desc_t);
|
||||
esp_bootloader_desc_t *bootloader_info = (esp_bootloader_desc_t *)img_info;
|
||||
if (bootloader_info->magic_byte != ESP_BOOTLOADER_DESC_MAGIC_BYTE) {
|
||||
ESP_LOGE(TAG, "Incorrect bootloader descriptor magic");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "This partition type (%d) is not supported", handle->partition.final->type);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
memcpy(new_img_info, img_info, img_info_len);
|
||||
@@ -614,6 +662,16 @@ esp_err_t esp_https_ota_perform(esp_https_ota_handle_t https_ota_handle)
|
||||
}
|
||||
}
|
||||
return _ota_write(handle, data_buf, binary_file_len);
|
||||
case ESP_HTTPS_OTA_RESUME:
|
||||
ESP_LOGD(TAG, "OTA resumption case");
|
||||
err = esp_ota_resume(handle->partition.staging, erase_size, handle->binary_file_len, &handle->update_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ota_resume failed (%s)", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
esp_ota_set_final_partition(handle->update_handle, handle->partition.final, handle->partition.finalize_with_copy);
|
||||
handle->state = ESP_HTTPS_OTA_IN_PROGRESS;
|
||||
/* falls through */
|
||||
case ESP_HTTPS_OTA_IN_PROGRESS:
|
||||
data_read = esp_http_client_read(handle->http_client,
|
||||
handle->ota_upgrade_buf,
|
||||
@@ -721,6 +779,7 @@ esp_err_t esp_https_ota_finish(esp_https_ota_handle_t https_ota_handle)
|
||||
err = esp_ota_end(handle->update_handle);
|
||||
/* falls through */
|
||||
case ESP_HTTPS_OTA_BEGIN:
|
||||
case ESP_HTTPS_OTA_RESUME:
|
||||
if (handle->ota_upgrade_buf) {
|
||||
free(handle->ota_upgrade_buf);
|
||||
}
|
||||
@@ -771,6 +830,7 @@ esp_err_t esp_https_ota_abort(esp_https_ota_handle_t https_ota_handle)
|
||||
err = esp_ota_abort(handle->update_handle);
|
||||
/* falls through */
|
||||
case ESP_HTTPS_OTA_BEGIN:
|
||||
case ESP_HTTPS_OTA_RESUME:
|
||||
if (handle->ota_upgrade_buf) {
|
||||
free(handle->ota_upgrade_buf);
|
||||
}
|
||||
@@ -827,6 +887,11 @@ esp_err_t esp_https_ota(const esp_https_ota_config_t *ota_config)
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (ota_config->ota_resumption) {
|
||||
ESP_LOGE(TAG, "OTA resumption is not supported in esp_https_ota API");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
esp_https_ota_handle_t https_ota_handle = NULL;
|
||||
esp_err_t err = esp_https_ota_begin(ota_config, &https_ota_handle);
|
||||
if (err != ESP_OK) {
|
||||
|
Reference in New Issue
Block a user