2022-01-07 21:10:08 +08:00
/*
2024-02-05 12:34:14 +05:30
* SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD
2022-01-07 21:10:08 +08:00
*
* SPDX-License-Identifier: Apache-2.0
*/
2018-07-12 17:45:44 +05:30
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <esp_https_ota.h>
#include <esp_log.h>
2019-10-14 17:56:21 -03:00
#include <esp_ota_ops.h>
2019-12-09 19:53:05 +05:30
#include <errno.h>
2020-11-17 12:48:35 +08:00
#include <sys/param.h>
2022-12-21 11:33:38 +05:30
#include <inttypes.h>
2018-07-12 17:45:44 +05:30
2022-11-02 11:05:19 +05:30
ESP_EVENT_DEFINE_BASE ( ESP_HTTPS_OTA_EVENT );
2022-01-28 16:29:29 +05:30
#define IMAGE_HEADER_SIZE (1024)
/* This is kept sufficiently large enough to cover image format headers
* and also this defines default minimum OTA buffer chunk size */
#define DEFAULT_OTA_BUF_SIZE (IMAGE_HEADER_SIZE)
_Static_assert ( DEFAULT_OTA_BUF_SIZE > ( sizeof ( esp_image_header_t ) + sizeof ( esp_image_segment_header_t ) + sizeof ( esp_app_desc_t ) + 1 ), "OTA data buffer too small" );
2021-02-02 13:25:40 +05:30
#define DEFAULT_REQUEST_SIZE (64 * 1024)
2023-03-23 15:31:27 +05:30
static const int DEFAULT_MAX_AUTH_RETRIES = 10 ;
2018-07-12 17:45:44 +05:30
static const char * TAG = "esp_https_ota" ;
2019-04-03 19:55:27 +05:30
typedef enum {
ESP_HTTPS_OTA_INIT ,
ESP_HTTPS_OTA_BEGIN ,
ESP_HTTPS_OTA_IN_PROGRESS ,
ESP_HTTPS_OTA_SUCCESS ,
} esp_https_ota_state ;
struct esp_https_ota_handle {
esp_ota_handle_t update_handle ;
2024-09-25 09:26:52 +03:00
struct { /*!< Details of staging and final partitions for OTA update */
const esp_partition_t * staging ; /*!< New image will be downloaded in this staging partition. If NULL then a free app partition (passive app partition) is selected as the staging partition. */
const esp_partition_t * final ; /*!< Final destination partition which is intended to be updated. Its type/subtype shall be used for verification. If NULL, staging partition is considered as the final partition. */
bool finalize_with_copy ; /*!< Flag to copy the staging image to the final at the end of OTA update */
} partition ;
2019-04-03 19:55:27 +05:30
esp_http_client_handle_t http_client ;
char * ota_upgrade_buf ;
size_t ota_upgrade_buf_size ;
int binary_file_len ;
2021-02-02 13:25:40 +05:30
int image_length ;
int max_http_request_size ;
2019-04-03 19:55:27 +05:30
esp_https_ota_state state ;
2020-07-21 18:23:28 +05:30
bool bulk_flash_erase ;
2021-02-02 13:25:40 +05:30
bool partial_http_download ;
2023-03-23 15:31:27 +05:30
int max_authorization_retries ;
2022-01-28 16:40:06 +05:30
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
decrypt_cb_t decrypt_cb ;
2022-03-10 12:02:42 +00:00
void * decrypt_user_ctx ;
2023-09-06 12:35:47 +05:30
uint16_t enc_img_header_size ;
2022-01-28 16:40:06 +05:30
#endif
2019-04-03 19:55:27 +05:30
};
typedef struct esp_https_ota_handle esp_https_ota_t ;
2022-03-17 13:50:30 +05:30
static bool redirection_required ( int status_code )
{
switch ( status_code ) {
case HttpStatus_MovedPermanently :
case HttpStatus_Found :
case HttpStatus_SeeOther :
case HttpStatus_TemporaryRedirect :
case HttpStatus_PermanentRedirect :
return true ;
default :
return false ;
}
return false ;
}
2019-04-03 19:55:27 +05:30
static bool process_again ( int status_code )
{
switch ( status_code ) {
case HttpStatus_MovedPermanently :
case HttpStatus_Found :
2022-03-17 13:50:30 +05:30
case HttpStatus_SeeOther :
2019-11-29 14:17:51 +11:00
case HttpStatus_TemporaryRedirect :
2022-03-17 13:50:30 +05:30
case HttpStatus_PermanentRedirect :
2019-04-03 19:55:27 +05:30
case HttpStatus_Unauthorized :
return true ;
default :
return false ;
}
return false ;
}
2023-03-23 15:31:27 +05:30
static esp_err_t _http_handle_response_code ( esp_https_ota_t * https_ota_handle , int status_code )
2019-04-03 19:55:27 +05:30
{
esp_err_t err ;
2022-03-17 13:50:30 +05:30
if ( redirection_required ( status_code )) {
2023-03-23 15:31:27 +05:30
err = esp_http_client_set_redirection ( https_ota_handle -> http_client );
2019-04-03 19:55:27 +05:30
if ( err != ESP_OK ) {
ESP_LOGE ( TAG , "URL redirection Failed" );
return err ;
}
} else if ( status_code == HttpStatus_Unauthorized ) {
2023-03-23 15:31:27 +05:30
if ( https_ota_handle -> max_authorization_retries == 0 ) {
ESP_LOGE ( TAG , "Reached max_authorization_retries (%d)" , status_code );
return ESP_FAIL ;
}
https_ota_handle -> max_authorization_retries -- ;
esp_http_client_add_auth ( https_ota_handle -> http_client );
2020-07-06 11:31:57 +05:30
} else if ( status_code == HttpStatus_NotFound || status_code == HttpStatus_Forbidden ) {
ESP_LOGE ( TAG , "File not found(%d)" , status_code );
return ESP_FAIL ;
2021-05-24 11:41:50 +05:30
} else if ( status_code >= HttpStatus_BadRequest && status_code < HttpStatus_InternalError ) {
ESP_LOGE ( TAG , "Client error (%d)" , status_code );
return ESP_FAIL ;
} else if ( status_code >= HttpStatus_InternalError ) {
ESP_LOGE ( TAG , "Server error (%d)" , status_code );
2020-07-06 11:31:57 +05:30
return ESP_FAIL ;
2019-04-03 19:55:27 +05:30
}
2020-11-10 18:40:01 +11:00
2022-01-28 16:29:29 +05:30
char upgrade_data_buf [ 256 ];
2020-02-13 13:43:58 +05:30
// process_again() returns true only in case of redirection.
2019-04-03 19:55:27 +05:30
if ( process_again ( status_code )) {
2020-02-13 13:43:58 +05:30
while ( 1 ) {
2019-12-09 19:53:05 +05:30
/*
2020-02-13 13:43:58 +05:30
* In case of redirection, esp_http_client_read() is called
* to clear the response buffer of http_client.
2019-12-09 19:53:05 +05:30
*/
2023-03-23 15:31:27 +05:30
int data_read = esp_http_client_read ( https_ota_handle -> http_client , upgrade_data_buf , sizeof ( upgrade_data_buf ));
2020-03-30 11:52:38 +05:30
if ( data_read <= 0 ) {
2020-02-13 13:43:58 +05:30
return ESP_OK ;
2019-04-03 19:55:27 +05:30
}
}
}
return ESP_OK ;
}
2023-03-23 15:31:27 +05:30
static esp_err_t _http_connect ( esp_https_ota_t * https_ota_handle )
2019-04-03 19:55:27 +05:30
{
esp_err_t err = ESP_FAIL ;
2019-08-15 21:10:00 +08:00
int status_code , header_ret ;
2019-04-03 19:55:27 +05:30
do {
2021-02-10 11:02:13 +05:30
char * post_data = NULL ;
/* Send POST request if body is set.
* Note: Sending POST request is not supported if partial_http_download
* is enabled
*/
2023-03-23 15:31:27 +05:30
int post_len = esp_http_client_get_post_field ( https_ota_handle -> http_client , & post_data );
err = esp_http_client_open ( https_ota_handle -> http_client , post_len );
2019-04-03 19:55:27 +05:30
if ( err != ESP_OK ) {
ESP_LOGE ( TAG , "Failed to open HTTP connection: %s" , esp_err_to_name ( err ));
return err ;
}
2021-02-10 11:02:13 +05:30
if ( post_len ) {
int write_len = 0 ;
while ( post_len > 0 ) {
2023-03-23 15:31:27 +05:30
write_len = esp_http_client_write ( https_ota_handle -> http_client , post_data , post_len );
2021-02-10 11:02:13 +05:30
if ( write_len < 0 ) {
ESP_LOGE ( TAG , "Write failed" );
return ESP_FAIL ;
}
2021-04-08 17:34:57 +05:30
post_len -= write_len ;
post_data += write_len ;
2021-02-10 11:02:13 +05:30
}
}
2023-03-23 15:31:27 +05:30
header_ret = esp_http_client_fetch_headers ( https_ota_handle -> http_client );
2019-08-15 21:10:00 +08:00
if ( header_ret < 0 ) {
return header_ret ;
}
2023-03-23 15:31:27 +05:30
status_code = esp_http_client_get_status_code ( https_ota_handle -> http_client );
err = _http_handle_response_code ( https_ota_handle , status_code );
2019-08-15 21:10:00 +08:00
if ( err != ESP_OK ) {
return err ;
2019-04-03 19:55:27 +05:30
}
} while ( process_again ( status_code ));
return err ;
}
static void _http_cleanup ( esp_http_client_handle_t client )
2018-07-12 17:45:44 +05:30
{
esp_http_client_close ( client );
esp_http_client_cleanup ( client );
}
2022-11-02 14:48:43 +05:30
// Table to lookup ota event name
static const char * ota_event_name_table [] = {
"ESP_HTTPS_OTA_START" ,
"ESP_HTTPS_OTA_CONNECTED" ,
"ESP_HTTPS_OTA_GET_IMG_DESC" ,
"ESP_HTTPS_OTA_VERIFY_CHIP_ID" ,
"ESP_HTTPS_OTA_DECRYPT_CB" ,
"ESP_HTTPS_OTA_WRITE_FLASH" ,
"ESP_HTTPS_OTA_UPDATE_BOOT_PARTITION" ,
"ESP_HTTPS_OTA_FINISH" ,
"ESP_HTTPS_OTA_ABORT" ,
};
2024-05-02 15:27:19 +05:30
#if CONFIG_ESP_HTTPS_OTA_EVENT_POST_TIMEOUT == -1
#define ESP_HTTPS_OTA_EVENT_POST_TIMEOUT portMAX_DELAY
#else
#define ESP_HTTPS_OTA_EVENT_POST_TIMEOUT pdMS_TO_TICKS(CONFIG_ESP_HTTPS_OTA_EVENT_POST_TIMEOUT)
#endif
2022-11-02 14:48:43 +05:30
static void esp_https_ota_dispatch_event ( int32_t event_id , const void * event_data , size_t event_data_size )
{
2024-05-02 15:27:19 +05:30
if ( esp_event_post ( ESP_HTTPS_OTA_EVENT , event_id , event_data , event_data_size , ESP_HTTPS_OTA_EVENT_POST_TIMEOUT ) != ESP_OK ) {
2022-11-02 14:48:43 +05:30
ESP_LOGE ( TAG , "Failed to post https_ota event: %s" , ota_event_name_table [ event_id ]);
}
}
2022-01-28 16:40:06 +05:30
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
static esp_err_t esp_https_ota_decrypt_cb ( esp_https_ota_t * handle , decrypt_cb_arg_t * args )
{
2022-11-02 14:48:43 +05:30
esp_https_ota_dispatch_event ( ESP_HTTPS_OTA_DECRYPT_CB , NULL , 0 );
2022-11-02 11:05:19 +05:30
2022-03-10 12:02:42 +00:00
esp_err_t ret = handle -> decrypt_cb ( args , handle -> decrypt_user_ctx );
2022-01-28 16:40:06 +05:30
if ( ret != ESP_OK ) {
ESP_LOGE ( TAG , "Decrypt callback failed %d" , ret );
return ret ;
}
if ( args -> data_out_len > 0 ) {
return ESP_OK ;
} else {
return ESP_HTTPS_OTA_IN_PROGRESS ;
}
}
static void esp_https_ota_decrypt_cb_free_buf ( void * buffer )
{
free ( buffer );
}
#endif // CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
2019-04-03 19:55:27 +05:30
static esp_err_t _ota_write ( esp_https_ota_t * https_ota_handle , const void * buffer , size_t buf_len )
{
if ( buffer == NULL || https_ota_handle == NULL ) {
return ESP_FAIL ;
}
esp_err_t err = esp_ota_write ( https_ota_handle -> update_handle , buffer , buf_len );
if ( err != ESP_OK ) {
2020-10-12 13:46:15 +02:00
ESP_LOGE ( TAG , "Error: esp_ota_write failed! err=0x%x" , err );
2019-04-03 19:55:27 +05:30
} else {
https_ota_handle -> binary_file_len += buf_len ;
ESP_LOGD ( TAG , "Written image length %d" , https_ota_handle -> binary_file_len );
err = ESP_ERR_HTTPS_OTA_IN_PROGRESS ;
}
2022-11-02 14:48:43 +05:30
esp_https_ota_dispatch_event ( ESP_HTTPS_OTA_WRITE_FLASH , ( void * )( & https_ota_handle -> binary_file_len ), sizeof ( int ));
2022-11-02 11:05:19 +05:30
2022-01-28 16:40:06 +05:30
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
esp_https_ota_decrypt_cb_free_buf (( void * ) buffer );
#endif
2019-04-03 19:55:27 +05:30
return err ;
}
2022-03-02 10:01:42 +05:30
static bool is_server_verification_enabled ( const esp_https_ota_config_t * ota_config ) {
2021-04-08 15:36:24 +05:30
return ( ota_config -> http_config -> cert_pem
|| ota_config -> http_config -> use_global_ca_store
2021-04-16 08:33:59 +08:00
|| ota_config -> http_config -> crt_bundle_attach != NULL );
2021-04-08 15:36:24 +05:30
}
2022-03-02 10:01:42 +05:30
esp_err_t esp_https_ota_begin ( const esp_https_ota_config_t * ota_config , esp_https_ota_handle_t * handle )
2019-04-03 19:55:27 +05:30
{
2022-11-02 14:48:43 +05:30
esp_https_ota_dispatch_event ( ESP_HTTPS_OTA_START , NULL , 0 );
2022-11-02 11:05:19 +05:30
2019-04-03 19:55:27 +05:30
esp_err_t err ;
2019-05-21 19:42:18 +02:00
2019-04-03 19:55:27 +05:30
if ( handle == NULL || ota_config == NULL || ota_config -> http_config == NULL ) {
ESP_LOGE ( TAG , "esp_https_ota_begin: Invalid argument" );
2019-05-21 19:42:18 +02:00
if ( handle ) {
* handle = NULL ;
}
2019-04-03 19:55:27 +05:30
return ESP_ERR_INVALID_ARG ;
}
2021-04-08 15:36:24 +05:30
if ( ! is_server_verification_enabled ( ota_config )) {
2022-01-28 16:41:22 +05:30
#if CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP
ESP_LOGW ( TAG , "Continuing with insecure option because CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is set." );
2021-04-19 19:26:10 +05:30
#else
2021-04-08 15:36:24 +05:30
ESP_LOGE ( TAG , "No option for server verification is enabled in esp_http_client config." );
2019-05-21 19:42:18 +02:00
* handle = NULL ;
2019-04-03 19:55:27 +05:30
return ESP_ERR_INVALID_ARG ;
#endif
2021-04-19 19:26:10 +05:30
}
2019-04-03 19:55:27 +05:30
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" );
2019-05-21 19:42:18 +02:00
* handle = NULL ;
2019-04-03 19:55:27 +05:30
return ESP_ERR_NO_MEM ;
}
2020-11-10 18:40:01 +11:00
2021-02-02 13:25:40 +05:30
https_ota_handle -> partial_http_download = ota_config -> partial_http_download ;
https_ota_handle -> max_http_request_size = ( ota_config -> max_http_request_size == 0 ) ? DEFAULT_REQUEST_SIZE : ota_config -> max_http_request_size ;
2023-03-23 15:31:27 +05:30
https_ota_handle -> max_authorization_retries = ota_config -> http_config -> max_authorization_retries ;
if ( https_ota_handle -> max_authorization_retries == 0 ) {
https_ota_handle -> max_authorization_retries = DEFAULT_MAX_AUTH_RETRIES ;
} else if ( https_ota_handle -> max_authorization_retries == - 1 ) {
https_ota_handle -> max_authorization_retries = 0 ;
}
2021-02-02 13:25:40 +05:30
2019-04-03 19:55:27 +05:30
/* Initiate HTTP Connection */
https_ota_handle -> http_client = esp_http_client_init ( ota_config -> http_config );
if ( https_ota_handle -> http_client == NULL ) {
ESP_LOGE ( TAG , "Failed to initialise HTTP connection" );
err = ESP_FAIL ;
goto failure ;
}
2022-09-01 12:04:40 +05:30
if ( ota_config -> http_client_init_cb ) {
err = ota_config -> http_client_init_cb ( https_ota_handle -> http_client );
if ( err != ESP_OK ) {
ESP_LOGE ( TAG , "http_client_init_cb returned 0x%x" , err );
goto http_cleanup ;
}
}
2021-02-02 13:25:40 +05:30
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 );
2022-01-07 21:10:08 +08:00
if ( err == ESP_OK ) {
int status = esp_http_client_get_status_code ( https_ota_handle -> http_client );
if ( status != HttpStatus_Ok ) {
ESP_LOGE ( TAG , "Received incorrect http status %d" , status );
err = ESP_FAIL ;
goto http_cleanup ;
}
} else {
ESP_LOGE ( TAG , "ESP HTTP client perform failed: %d" , err );
2021-02-02 13:25:40 +05:30
goto http_cleanup ;
}
2022-01-07 21:10:08 +08:00
2021-02-02 13:25:40 +05:30
https_ota_handle -> image_length = esp_http_client_get_content_length ( https_ota_handle -> http_client );
2023-09-06 12:35:47 +05:30
#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*/
https_ota_handle -> image_length -= ota_config -> enc_img_header_size ;
#endif
2021-02-02 13:25:40 +05:30
esp_http_client_close ( https_ota_handle -> http_client );
if ( https_ota_handle -> image_length > https_ota_handle -> max_http_request_size ) {
char * header_val = NULL ;
asprintf ( & header_val , "bytes=0-%d" , https_ota_handle -> max_http_request_size - 1 );
if ( header_val == NULL ) {
ESP_LOGE ( TAG , "Failed to allocate memory for HTTP header" );
err = ESP_ERR_NO_MEM ;
goto http_cleanup ;
}
esp_http_client_set_header ( https_ota_handle -> http_client , "Range" , header_val );
free ( header_val );
}
esp_http_client_set_method ( https_ota_handle -> http_client , HTTP_METHOD_GET );
}
2023-03-23 15:31:27 +05:30
err = _http_connect ( https_ota_handle );
2019-04-03 19:55:27 +05:30
if ( err != ESP_OK ) {
ESP_LOGE ( TAG , "Failed to establish HTTP connection" );
goto http_cleanup ;
2022-11-02 11:05:19 +05:30
} else {
2022-11-02 14:48:43 +05:30
esp_https_ota_dispatch_event ( ESP_HTTPS_OTA_CONNECTED , NULL , 0 );
2019-04-03 19:55:27 +05:30
}
2021-04-15 17:24:17 +02:00
if ( ! https_ota_handle -> partial_http_download ) {
https_ota_handle -> image_length = esp_http_client_get_content_length ( https_ota_handle -> http_client );
2023-09-06 12:35:47 +05:30
#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*/
https_ota_handle -> image_length -= ota_config -> enc_img_header_size ;
#endif
2021-04-15 17:24:17 +02:00
}
2024-09-25 09:26:52 +03:00
https_ota_handle -> partition . staging = NULL ;
2019-04-03 19:55:27 +05:30
ESP_LOGI ( TAG , "Starting OTA..." );
2024-09-25 09:26:52 +03:00
if ( ota_config -> partition . staging != NULL ) {
https_ota_handle -> partition . staging = esp_partition_verify ( ota_config -> partition . staging );
} else {
https_ota_handle -> partition . staging = esp_ota_get_next_update_partition ( NULL );
}
if ( https_ota_handle -> partition . staging == NULL ) {
ESP_LOGE ( TAG , "Given staging partition or another suitable Passive OTA partition could not be found" );
2019-04-03 19:55:27 +05:30
err = ESP_FAIL ;
goto http_cleanup ;
}
2024-09-24 19:37:46 +03:00
ESP_LOGI ( TAG , "Writing to <%s> partition at offset 0x%" PRIx32 ,
2024-09-25 09:26:52 +03:00
https_ota_handle -> partition . staging -> label , https_ota_handle -> partition . staging -> address );
if ( ota_config -> partition . final == NULL ) {
https_ota_handle -> partition . final = https_ota_handle -> partition . staging ;
} else {
if ( ota_config -> partition . staging != ota_config -> partition . final ) {
const esp_partition_t * final = esp_partition_verify ( ota_config -> partition . final );
if ( final == NULL ) {
ESP_LOGE ( TAG , "Given final partition not found" );
err = ESP_FAIL ;
goto http_cleanup ;
}
https_ota_handle -> partition . final = final ;
https_ota_handle -> partition . finalize_with_copy = ota_config -> partition . finalize_with_copy ;
}
}
2019-04-03 19:55:27 +05:30
2020-11-17 12:48:35 +08:00
const int alloc_size = MAX ( ota_config -> http_config -> buffer_size , DEFAULT_OTA_BUF_SIZE );
2023-12-12 18:44:15 +05:30
if ( ota_config -> buffer_caps != 0 ) {
https_ota_handle -> ota_upgrade_buf = ( char * ) heap_caps_malloc ( alloc_size , ota_config -> buffer_caps );
} else {
https_ota_handle -> ota_upgrade_buf = ( char * ) malloc ( alloc_size );
}
2019-04-03 19:55:27 +05:30
if ( ! https_ota_handle -> ota_upgrade_buf ) {
ESP_LOGE ( TAG , "Couldn't allocate memory to upgrade data buffer" );
err = ESP_ERR_NO_MEM ;
goto http_cleanup ;
}
2022-01-28 16:40:06 +05:30
#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 ;
2022-03-10 12:02:42 +00:00
https_ota_handle -> decrypt_user_ctx = ota_config -> decrypt_user_ctx ;
2023-09-06 12:35:47 +05:30
https_ota_handle -> enc_img_header_size = ota_config -> enc_img_header_size ;
2022-01-28 16:40:06 +05:30
#endif
2019-04-03 19:55:27 +05:30
https_ota_handle -> ota_upgrade_buf_size = alloc_size ;
2020-07-21 18:23:28 +05:30
https_ota_handle -> bulk_flash_erase = ota_config -> bulk_flash_erase ;
2019-04-03 19:55:27 +05:30
https_ota_handle -> binary_file_len = 0 ;
* handle = ( esp_https_ota_handle_t ) https_ota_handle ;
https_ota_handle -> state = ESP_HTTPS_OTA_BEGIN ;
return ESP_OK ;
http_cleanup :
_http_cleanup ( https_ota_handle -> http_client );
failure :
free ( https_ota_handle );
2019-05-21 19:42:18 +02:00
* handle = NULL ;
2019-04-03 19:55:27 +05:30
return err ;
}
2022-01-10 18:32:37 +05:30
static esp_err_t read_header ( esp_https_ota_t * handle )
2019-04-03 19:55:27 +05:30
{
2019-12-09 19:53:05 +05:30
/*
* `data_read_size` holds number of bytes needed to read complete header.
* `bytes_read` holds number of bytes read.
*/
2019-04-03 19:55:27 +05:30
int data_read_size = IMAGE_HEADER_SIZE ;
2019-12-09 19:53:05 +05:30
int data_read = 0 , bytes_read = 0 ;
/*
* while loop is added to download complete image headers, even if the headers
* are not sent in a single packet.
*/
2021-02-02 13:25:40 +05:30
while ( data_read_size > 0 && ! esp_http_client_is_complete_data_received ( handle -> http_client )) {
2019-12-09 19:53:05 +05:30
data_read = esp_http_client_read ( handle -> http_client ,
( handle -> ota_upgrade_buf + bytes_read ),
data_read_size );
2022-03-07 11:05:43 +05:30
if ( data_read < 0 ) {
if ( data_read == - ESP_ERR_HTTP_EAGAIN ) {
ESP_LOGD ( TAG , "ESP_ERR_HTTP_EAGAIN invoked: Call timed out before data was ready" );
continue ;
}
2019-12-09 19:53:05 +05:30
ESP_LOGE ( TAG , "Connection closed, errno = %d" , errno );
break ;
}
data_read_size -= data_read ;
bytes_read += data_read ;
2019-04-03 19:55:27 +05:30
}
2019-12-09 19:53:05 +05:30
if ( data_read_size > 0 ) {
ESP_LOGE ( TAG , "Complete headers were not received" );
2019-04-03 19:55:27 +05:30
return ESP_FAIL ;
}
2019-12-09 19:53:05 +05:30
handle -> binary_file_len = bytes_read ;
2022-01-10 18:32:37 +05:30
return ESP_OK ;
}
2024-09-25 09:26:52 +03:00
static esp_err_t get_description_from_image ( esp_https_ota_handle_t https_ota_handle , void * new_img_info )
2022-01-10 18:32:37 +05:30
{
2022-11-02 14:48:43 +05:30
esp_https_ota_dispatch_event ( ESP_HTTPS_OTA_GET_IMG_DESC , NULL , 0 );
2022-11-02 11:05:19 +05:30
2022-03-09 12:33:39 +05:30
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
// This API is not supported in case firmware image is encrypted in nature.
// It is recommended to retrieve image description through decryption callback in application layer.
return ESP_ERR_NOT_SUPPORTED ;
#endif
2022-01-10 18:32:37 +05:30
esp_https_ota_t * handle = ( esp_https_ota_t * ) https_ota_handle ;
2024-09-25 09:26:52 +03:00
if ( handle == NULL || new_img_info == NULL ) {
2024-02-05 12:34:14 +05:30
ESP_LOGE ( TAG , "esp_https_ota_get_img_desc: Invalid argument" );
2022-01-10 18:32:37 +05:30
return ESP_ERR_INVALID_ARG ;
}
if ( handle -> state < ESP_HTTPS_OTA_BEGIN ) {
2024-02-05 12:34:14 +05:30
ESP_LOGE ( TAG , "esp_https_ota_get_img_desc: Invalid state" );
2022-01-28 16:31:47 +05:30
return ESP_ERR_INVALID_STATE ;
2022-01-10 18:32:37 +05:30
}
if ( read_header ( handle ) != ESP_OK ) {
return ESP_FAIL ;
}
2022-01-28 16:31:47 +05:30
2024-09-25 09:26:52 +03:00
void * img_info = ( void * ) & handle -> ota_upgrade_buf [ sizeof ( esp_image_header_t ) + sizeof ( esp_image_segment_header_t )];
unsigned img_info_len ;
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 );
2022-01-28 16:31:47 +05:30
return ESP_FAIL ;
}
2024-09-25 09:26:52 +03:00
memcpy ( new_img_info , img_info , img_info_len );
2020-11-10 18:40:01 +11:00
return ESP_OK ;
2019-04-03 19:55:27 +05:30
}
2024-09-25 09:26:52 +03:00
esp_err_t esp_https_ota_get_img_desc ( esp_https_ota_handle_t https_ota_handle , esp_app_desc_t * new_app_info )
{
return get_description_from_image ( https_ota_handle , new_app_info );
}
esp_err_t esp_https_ota_get_bootloader_img_desc ( esp_https_ota_handle_t https_ota_handle , esp_bootloader_desc_t * new_img_info )
{
return get_description_from_image ( https_ota_handle , new_img_info );
}
2022-01-28 16:31:47 +05:30
static esp_err_t esp_ota_verify_chip_id ( const void * arg )
2022-01-10 18:32:37 +05:30
{
2022-01-28 16:31:47 +05:30
esp_image_header_t * data = ( esp_image_header_t * )( arg );
2022-11-02 14:48:43 +05:30
esp_https_ota_dispatch_event ( ESP_HTTPS_OTA_VERIFY_CHIP_ID , ( void * )( & data -> chip_id ), sizeof ( esp_chip_id_t ));
2022-01-10 18:32:37 +05:30
if ( data -> chip_id != CONFIG_IDF_FIRMWARE_CHIP_ID ) {
ESP_LOGE ( TAG , "Mismatch chip id, expected %d, found %d" , CONFIG_IDF_FIRMWARE_CHIP_ID , data -> chip_id );
return ESP_ERR_INVALID_VERSION ;
}
return ESP_OK ;
}
2019-04-03 19:55:27 +05:30
esp_err_t esp_https_ota_perform ( esp_https_ota_handle_t https_ota_handle )
{
esp_https_ota_t * handle = ( esp_https_ota_t * ) https_ota_handle ;
if ( handle == NULL ) {
ESP_LOGE ( TAG , "esp_https_ota_perform: Invalid argument" );
return ESP_ERR_INVALID_ARG ;
}
if ( handle -> state < ESP_HTTPS_OTA_BEGIN ) {
ESP_LOGE ( TAG , "esp_https_ota_perform: Invalid state" );
return ESP_FAIL ;
}
esp_err_t err ;
int data_read ;
2023-12-12 18:44:15 +05:30
const int erase_size = handle -> bulk_flash_erase ? ( handle -> image_length > 0 ? handle -> image_length : OTA_SIZE_UNKNOWN ) : OTA_WITH_SEQUENTIAL_WRITES ;
2019-04-03 19:55:27 +05:30
switch ( handle -> state ) {
case ESP_HTTPS_OTA_BEGIN :
2024-09-25 09:26:52 +03:00
err = esp_ota_begin ( handle -> partition . staging , erase_size , & handle -> update_handle );
2019-04-03 19:55:27 +05:30
if ( err != ESP_OK ) {
ESP_LOGE ( TAG , "esp_ota_begin failed (%s)" , esp_err_to_name ( err ));
return err ;
}
2024-09-25 09:26:52 +03:00
esp_ota_set_final_partition ( handle -> update_handle , handle -> partition . final , handle -> partition . finalize_with_copy );
2019-04-03 19:55:27 +05:30
handle -> state = ESP_HTTPS_OTA_IN_PROGRESS ;
2024-02-05 12:34:14 +05:30
/* In case `esp_https_ota_get_img_desc` was invoked first,
2019-04-03 19:55:27 +05:30
then the image data read there should be written to OTA partition
*/
2022-01-10 18:32:37 +05:30
int binary_file_len = 0 ;
2019-04-03 19:55:27 +05:30
if ( handle -> binary_file_len ) {
2022-01-10 18:32:37 +05:30
binary_file_len = handle -> binary_file_len ;
} else {
if ( read_header ( handle ) != ESP_OK ) {
return ESP_FAIL ;
}
binary_file_len = IMAGE_HEADER_SIZE ;
2019-04-03 19:55:27 +05:30
}
2023-08-03 17:07:25 +05:30
/*
* Header length gets added to handle->binary_file_len in _ota_write
* Clear handle->binary_file_len to avoid additional bytes in upgrade image size calculation
*/
handle -> binary_file_len = 0 ;
2022-01-28 16:40:06 +05:30
const void * data_buf = ( const void * ) handle -> ota_upgrade_buf ;
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
decrypt_cb_arg_t args = {};
args . data_in = handle -> ota_upgrade_buf ;
args . data_in_len = binary_file_len ;
err = esp_https_ota_decrypt_cb ( handle , & args );
if ( err == ESP_OK ) {
data_buf = args . data_out ;
binary_file_len = args . data_out_len ;
} else {
ESP_LOGE ( TAG , "Decryption of image header failed" );
return ESP_FAIL ;
}
#endif // CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
2024-09-25 09:26:52 +03:00
if ( handle -> partition . final -> type == ESP_PARTITION_TYPE_APP || handle -> partition . final -> type == ESP_PARTITION_TYPE_BOOTLOADER ) {
err = esp_ota_verify_chip_id ( data_buf );
if ( err != ESP_OK ) {
return err ;
}
2022-01-10 18:32:37 +05:30
}
2022-01-28 16:40:06 +05:30
return _ota_write ( handle , data_buf , binary_file_len );
2019-04-03 19:55:27 +05:30
case ESP_HTTPS_OTA_IN_PROGRESS :
data_read = esp_http_client_read ( handle -> http_client ,
handle -> ota_upgrade_buf ,
handle -> ota_upgrade_buf_size );
if ( data_read == 0 ) {
2019-12-09 19:53:05 +05:30
/*
2021-02-02 13:25:40 +05:30
* esp_http_client_is_complete_data_received is added to check whether
2020-01-27 17:33:36 +05:30
* complete image is received.
2019-12-09 19:53:05 +05:30
*/
2022-03-07 11:05:43 +05:30
if ( ! esp_http_client_is_complete_data_received ( handle -> http_client )) {
ESP_LOGE ( TAG , "Connection closed before complete data was received!" );
2019-12-09 19:53:05 +05:30
return ESP_FAIL ;
}
2021-08-06 12:55:00 +08:00
ESP_LOGD ( TAG , "Connection closed" );
2019-04-03 19:55:27 +05:30
} else if ( data_read > 0 ) {
2022-01-28 16:40:06 +05:30
const void * data_buf = ( const void * ) handle -> ota_upgrade_buf ;
int data_len = data_read ;
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
decrypt_cb_arg_t args = {};
args . data_in = handle -> ota_upgrade_buf ;
args . data_in_len = data_read ;
err = esp_https_ota_decrypt_cb ( handle , & args );
if ( err == ESP_OK ) {
data_buf = args . data_out ;
data_len = args . data_out_len ;
} else {
return err ;
}
#endif // CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
return _ota_write ( handle , data_buf , data_len );
2020-03-30 11:52:38 +05:30
} else {
2022-03-07 11:05:43 +05:30
if ( data_read == - ESP_ERR_HTTP_EAGAIN ) {
ESP_LOGD ( TAG , "ESP_ERR_HTTP_EAGAIN invoked: Call timed out before data was ready" );
return ESP_ERR_HTTPS_OTA_IN_PROGRESS ;
}
2021-08-02 17:04:14 +05:30
ESP_LOGE ( TAG , "data read %d, errno %d" , data_read , errno );
2020-03-30 11:52:38 +05:30
return ESP_FAIL ;
2019-04-03 19:55:27 +05:30
}
2021-02-02 13:25:40 +05:30
if ( ! handle -> partial_http_download || ( handle -> partial_http_download && handle -> image_length == handle -> binary_file_len )) {
handle -> state = ESP_HTTPS_OTA_SUCCESS ;
}
2019-04-03 19:55:27 +05:30
break ;
default :
ESP_LOGE ( TAG , "Invalid ESP HTTPS OTA State" );
return ESP_FAIL ;
break ;
}
2021-02-02 13:25:40 +05:30
if ( handle -> partial_http_download ) {
if ( handle -> state == ESP_HTTPS_OTA_IN_PROGRESS && handle -> image_length > handle -> binary_file_len ) {
esp_http_client_close ( handle -> http_client );
char * header_val = NULL ;
2023-09-06 12:35:47 +05:30
int header_size = 0 ;
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB
header_size = handle -> enc_img_header_size ;
#endif
2021-02-02 13:25:40 +05:30
if (( handle -> image_length - handle -> binary_file_len ) > handle -> max_http_request_size ) {
2023-09-06 12:35:47 +05:30
asprintf ( & header_val , "bytes=%d-%d" , handle -> binary_file_len + header_size , ( handle -> binary_file_len + header_size + handle -> max_http_request_size - 1 ));
2021-02-02 13:25:40 +05:30
} else {
2023-09-06 12:35:47 +05:30
asprintf ( & header_val , "bytes=%d-" , handle -> binary_file_len + header_size );
2021-02-02 13:25:40 +05:30
}
if ( header_val == NULL ) {
ESP_LOGE ( TAG , "Failed to allocate memory for HTTP header" );
return ESP_ERR_NO_MEM ;
}
esp_http_client_set_header ( handle -> http_client , "Range" , header_val );
free ( header_val );
2023-03-23 15:31:27 +05:30
err = _http_connect ( handle );
2021-02-02 13:25:40 +05:30
if ( err != ESP_OK ) {
ESP_LOGE ( TAG , "Failed to establish HTTP connection" );
return ESP_FAIL ;
}
2021-08-06 12:55:00 +08:00
ESP_LOGD ( TAG , "Connection start" );
2021-02-02 13:25:40 +05:30
return ESP_ERR_HTTPS_OTA_IN_PROGRESS ;
}
}
2019-04-03 19:55:27 +05:30
return ESP_OK ;
}
2019-08-15 21:10:00 +08:00
bool esp_https_ota_is_complete_data_received ( esp_https_ota_handle_t https_ota_handle )
{
2021-02-02 13:25:40 +05:30
bool ret = false ;
2019-08-15 21:10:00 +08:00
esp_https_ota_t * handle = ( esp_https_ota_t * ) https_ota_handle ;
2021-02-02 13:25:40 +05:30
if ( handle -> partial_http_download ) {
ret = ( handle -> image_length == handle -> binary_file_len );
} else {
ret = esp_http_client_is_complete_data_received ( handle -> http_client );
}
return ret ;
2019-08-15 21:10:00 +08:00
}
2019-04-03 19:55:27 +05:30
esp_err_t esp_https_ota_finish ( esp_https_ota_handle_t https_ota_handle )
{
esp_https_ota_t * handle = ( esp_https_ota_t * ) https_ota_handle ;
if ( handle == NULL ) {
return ESP_ERR_INVALID_ARG ;
}
if ( handle -> state < ESP_HTTPS_OTA_BEGIN ) {
return ESP_FAIL ;
}
esp_err_t err = ESP_OK ;
switch ( handle -> state ) {
case ESP_HTTPS_OTA_SUCCESS :
case ESP_HTTPS_OTA_IN_PROGRESS :
err = esp_ota_end ( handle -> update_handle );
/* falls through */
case ESP_HTTPS_OTA_BEGIN :
2019-05-21 19:42:18 +02:00
if ( handle -> ota_upgrade_buf ) {
free ( handle -> ota_upgrade_buf );
}
if ( handle -> http_client ) {
_http_cleanup ( handle -> http_client );
}
2019-04-03 19:55:27 +05:30
break ;
default :
ESP_LOGE ( TAG , "Invalid ESP HTTPS OTA State" );
break ;
}
2024-09-25 09:26:52 +03:00
if (( handle -> partition . final -> type == ESP_PARTITION_TYPE_APP ) && ( err == ESP_OK ) && ( handle -> state == ESP_HTTPS_OTA_SUCCESS )) {
if ( handle -> partition . final -> subtype >= ESP_PARTITION_SUBTYPE_APP_OTA_0 && handle -> partition . final -> subtype < ESP_PARTITION_SUBTYPE_APP_OTA_MAX ) {
// Do not allow the boot partition to be set as the Factory partition, because ota_data will be erased in this case.
// The user can call it afterward if needed.
err = esp_ota_set_boot_partition ( handle -> partition . staging );
}
2019-04-03 19:55:27 +05:30
if ( err != ESP_OK ) {
2020-10-12 13:46:15 +02:00
ESP_LOGE ( TAG , "esp_ota_set_boot_partition failed! err=0x%x" , err );
2022-11-02 11:05:19 +05:30
} else {
2024-09-25 09:26:52 +03:00
esp_https_ota_dispatch_event ( ESP_HTTPS_OTA_UPDATE_BOOT_PARTITION , ( void * )( & handle -> partition . final -> subtype ), sizeof ( esp_partition_subtype_t ));
2019-04-03 19:55:27 +05:30
}
}
2019-05-21 19:42:18 +02:00
free ( handle );
2022-11-02 14:48:43 +05:30
esp_https_ota_dispatch_event ( ESP_HTTPS_OTA_FINISH , NULL , 0 );
2022-11-02 11:05:19 +05:30
2019-04-03 19:55:27 +05:30
return err ;
}
2020-08-14 15:54:15 +05:30
esp_err_t esp_https_ota_abort ( esp_https_ota_handle_t https_ota_handle )
{
2022-11-02 14:48:43 +05:30
esp_https_ota_dispatch_event ( ESP_HTTPS_OTA_ABORT , NULL , 0 );
2022-11-02 11:05:19 +05:30
2020-08-14 15:54:15 +05:30
esp_https_ota_t * handle = ( esp_https_ota_t * ) https_ota_handle ;
if ( handle == NULL ) {
return ESP_ERR_INVALID_ARG ;
}
if ( handle -> state < ESP_HTTPS_OTA_BEGIN ) {
return ESP_FAIL ;
}
esp_err_t err = ESP_OK ;
switch ( handle -> state ) {
case ESP_HTTPS_OTA_SUCCESS :
case ESP_HTTPS_OTA_IN_PROGRESS :
err = esp_ota_abort ( handle -> update_handle );
/* falls through */
case ESP_HTTPS_OTA_BEGIN :
if ( handle -> ota_upgrade_buf ) {
free ( handle -> ota_upgrade_buf );
}
if ( handle -> http_client ) {
_http_cleanup ( handle -> http_client );
}
break ;
default :
err = ESP_ERR_INVALID_STATE ;
ESP_LOGE ( TAG , "Invalid ESP HTTPS OTA State" );
break ;
}
free ( handle );
return err ;
}
2024-08-30 11:29:05 +05:30
int esp_https_ota_get_status_code ( esp_https_ota_handle_t https_ota_handle )
{
esp_https_ota_t * handle = ( esp_https_ota_t * ) https_ota_handle ;
if ( handle == NULL || handle -> http_client == NULL ) {
return - 1 ;
}
return esp_http_client_get_status_code ( handle -> http_client );
}
2019-04-03 19:55:27 +05:30
int esp_https_ota_get_image_len_read ( esp_https_ota_handle_t https_ota_handle )
{
esp_https_ota_t * handle = ( esp_https_ota_t * ) https_ota_handle ;
if ( handle == NULL ) {
return - 1 ;
}
if ( handle -> state < ESP_HTTPS_OTA_IN_PROGRESS ) {
return - 1 ;
}
return handle -> binary_file_len ;
}
2021-04-15 17:24:17 +02:00
int esp_https_ota_get_image_size ( esp_https_ota_handle_t https_ota_handle )
{
esp_https_ota_t * handle = ( esp_https_ota_t * ) https_ota_handle ;
if ( handle == NULL ) {
return - 1 ;
}
if ( handle -> state < ESP_HTTPS_OTA_BEGIN ) {
return - 1 ;
}
return handle -> image_length ;
}
2022-03-02 10:01:42 +05:30
esp_err_t esp_https_ota ( const esp_https_ota_config_t * ota_config )
2018-07-12 17:45:44 +05:30
{
2022-03-02 10:01:42 +05:30
if ( ota_config == NULL || ota_config -> http_config == NULL ) {
ESP_LOGE ( TAG , "esp_https_ota: Invalid argument" );
2018-07-12 17:45:44 +05:30
return ESP_ERR_INVALID_ARG ;
2020-11-10 18:40:01 +11:00
}
2018-07-12 17:45:44 +05:30
2019-04-03 19:55:27 +05:30
esp_https_ota_handle_t https_ota_handle = NULL ;
2022-03-02 10:01:42 +05:30
esp_err_t err = esp_https_ota_begin ( ota_config , & https_ota_handle );
2019-04-03 19:55:27 +05:30
if ( https_ota_handle == NULL ) {
2018-07-12 17:45:44 +05:30
return ESP_FAIL ;
}
while ( 1 ) {
2019-04-03 19:55:27 +05:30
err = esp_https_ota_perform ( https_ota_handle );
if ( err != ESP_ERR_HTTPS_OTA_IN_PROGRESS ) {
2018-07-12 17:45:44 +05:30
break ;
}
}
if ( err != ESP_OK ) {
2020-08-14 15:54:15 +05:30
esp_https_ota_abort ( https_ota_handle );
2018-07-12 17:45:44 +05:30
return err ;
2020-08-14 15:54:15 +05:30
}
esp_err_t ota_finish_err = esp_https_ota_finish ( https_ota_handle );
if ( ota_finish_err != ESP_OK ) {
2019-04-03 19:55:27 +05:30
return ota_finish_err ;
2018-07-12 17:45:44 +05:30
}
return ESP_OK ;
2019-08-15 21:10:00 +08:00
}