上一回开始对于乐鑫官网例程中OTA代码主要流程中的核心部分——esp_https_ota_perform函数进行解析,讲解了函数的前两段内容。本回继续往下进行解析。为了便于理解和回顾,再次贴出该函数源码,在C:\Espressif\frameworks\esp-idf-v5.2.1\components\esp_https_ota\src\esp_https_ota.c中,如下:
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;const int erase_size = handle->bulk_flash_erase ? OTA_SIZE_UNKNOWN : OTA_WITH_SEQUENTIAL_WRITES;switch (handle->state) {case ESP_HTTPS_OTA_BEGIN:err = esp_ota_begin(handle->update_partition, erase_size, &handle->update_handle);if (err != ESP_OK) {ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));return err;}handle->state = ESP_HTTPS_OTA_IN_PROGRESS;/* In case `esp_https_ota_read_img_desc` was invoked first,then the image data read there should be written to OTA partition*/int binary_file_len = 0;if (handle->binary_file_len) {binary_file_len = handle->binary_file_len;} else {if (read_header(handle) != ESP_OK) {return ESP_FAIL;}binary_file_len = IMAGE_HEADER_SIZE;}/** 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;const void *data_buf = (const void *) handle->ota_upgrade_buf;
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CBdecrypt_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_CBerr = esp_ota_verify_chip_id(data_buf);if (err != ESP_OK) {return err;}return _ota_write(handle, data_buf, binary_file_len);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) {/** esp_http_client_is_complete_data_received is added to check whether* complete image is received.*/if (!esp_http_client_is_complete_data_received(handle->http_client)) {ESP_LOGE(TAG, "Connection closed before complete data was received!");return ESP_FAIL;}ESP_LOGD(TAG, "Connection closed");} else if (data_read > 0) {const void *data_buf = (const void *) handle->ota_upgrade_buf;int data_len = data_read;
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CBdecrypt_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_CBreturn _ota_write(handle, data_buf, data_len);} else {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;}ESP_LOGE(TAG, "data read %d, errno %d", data_read, errno);return ESP_FAIL;}if (!handle->partial_http_download || (handle->partial_http_download && handle->image_length == handle->binary_file_len)) {handle->state = ESP_HTTPS_OTA_SUCCESS;}break;default:ESP_LOGE(TAG, "Invalid ESP HTTPS OTA State");return ESP_FAIL;break;}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;int header_size = 0;
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CBheader_size = handle->enc_img_header_size;
#endifif ((handle->image_length - handle->binary_file_len) > handle->max_http_request_size) {asprintf(&header_val, "bytes=%d-%d", handle->binary_file_len + header_size, (handle->binary_file_len + header_size + handle->max_http_request_size - 1));} else {asprintf(&header_val, "bytes=%d-", handle->binary_file_len + header_size);}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);err = _http_connect(handle);if (err != ESP_OK) {ESP_LOGE(TAG, "Failed to establish HTTP connection");return ESP_FAIL;}ESP_LOGD(TAG, "Connection start");return ESP_ERR_HTTPS_OTA_IN_PROGRESS;}}return ESP_OK;
}
3)switch中的ESP_HTTPS_OTA_BEGIN
代码片段如下:
switch (handle->state) {case ESP_HTTPS_OTA_BEGIN:err = esp_ota_begin(handle->update_partition, erase_size, &handle->update_handle);if (err != ESP_OK) {ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));return err;}handle->state = ESP_HTTPS_OTA_IN_PROGRESS;/* In case `esp_https_ota_read_img_desc` was invoked first,then the image data read there should be written to OTA partition*/int binary_file_len = 0;if (handle->binary_file_len) {binary_file_len = handle->binary_file_len;} else {if (read_header(handle) != ESP_OK) {return ESP_FAIL;}binary_file_len = IMAGE_HEADER_SIZE;}/** 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;const void *data_buf = (const void *) handle->ota_upgrade_buf;
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CBdecrypt_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_CBerr = esp_ota_verify_chip_id(data_buf);if (err != ESP_OK) {return err;}return _ota_write(handle, data_buf, binary_file_len);……}
上一回已经讲到,handle->state即https_ota_handle->state的值在esp_https_ota_begin函数中被赋值(初始化)为ESP_HTTPS_OTA_BEGIN。那么,这里一上来当然应该进入switch中的case ESP_HTTPS_OTA_BEGIN。
ESP_HTTPS_OTA_BEGIN这个case中,也包括了很多步骤,仍然一个一个来解析。
3.1)esp_ota_begin
代码片段为:
err = esp_ota_begin(handle->update_partition, erase_size, &handle->update_handle);if (err != ESP_OK) {ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));return err;}
esp_ota_begin函数在C:\Espressif\frameworks\esp-idf-v5.2.1\components\app_update\esp_ota_ops.c中,代码如下:
esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp_ota_handle_t *out_handle)
{ota_ops_entry_t *new_entry;esp_err_t ret = ESP_OK;if ((partition == NULL) || (out_handle == NULL)) {return ESP_ERR_INVALID_ARG;}partition = esp_partition_verify(partition);if (partition == NULL) {return ESP_ERR_NOT_FOUND;}if (!is_ota_partition(partition)) {return ESP_ERR_INVALID_ARG;}const esp_partition_t* running_partition = esp_ota_get_running_partition();if (partition == running_partition) {return ESP_ERR_OTA_PARTITION_CONFLICT;}#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLEesp_ota_img_states_t ota_state_running_part;if (esp_ota_get_state_partition(running_partition, &ota_state_running_part) == ESP_OK) {if (ota_state_running_part == ESP_OTA_IMG_PENDING_VERIFY) {ESP_LOGE(TAG, "Running app has not confirmed state (ESP_OTA_IMG_PENDING_VERIFY)");return ESP_ERR_OTA_ROLLBACK_INVALID_STATE;}}
#endifif (image_size != OTA_WITH_SEQUENTIAL_WRITES) {// If input image size is 0 or OTA_SIZE_UNKNOWN, erase entire partitionif ((image_size == 0) || (image_size == OTA_SIZE_UNKNOWN)) {ret = esp_partition_erase_range(partition, 0, partition->size);} else {const int aligned_erase_size = (image_size + SPI_FLASH_SEC_SIZE - 1) & ~(SPI_FLASH_SEC_SIZE - 1);ret = esp_partition_erase_range(partition, 0, aligned_erase_size);}if (ret != ESP_OK) {return ret;}}new_entry = (ota_ops_entry_t *) calloc(sizeof(ota_ops_entry_t), 1);if (new_entry == NULL) {return ESP_ERR_NO_MEM;}LIST_INSERT_HEAD(&s_ota_ops_entries_head, new_entry, entries);new_entry->part = partition;new_entry->handle = ++s_ota_ops_last_handle;new_entry->need_erase = (image_size == OTA_WITH_SEQUENTIAL_WRITES);*out_handle = new_entry->handle;return ESP_OK;
}
- esp_partition_verify函数
esp_partition_verify函数在C:\Espressif\frameworks\esp-idf-v5.2.1\components\esp_partition\partition.c中,代码如下:
const esp_partition_t *esp_partition_verify(const esp_partition_t *partition)
{assert(partition != NULL);const char *label = (strlen(partition->label) > 0) ? partition->label : NULL;esp_partition_iterator_t it = esp_partition_find(partition->type,partition->subtype,label);while (it != NULL) {const esp_partition_t *p = esp_partition_get(it);/* Can't memcmp() whole structure here as padding contents may be different */if (p->flash_chip == partition->flash_chip&& p->address == partition->address&& partition->size == p->size&& partition->encrypted == p->encrypted) {esp_partition_iterator_release(it);return p;}it = esp_partition_next(it);}esp_partition_iterator_release(it);return NULL;
}
esp_partition_verify函数的作用是:给定一个指向分区数据的指针,验证分区表中是否存在此分区(保证所有字段都匹配)。此函数还可用于获取RAM缓冲区中的分区数据,并将其转换为指向存储在闪存中的永久分区数据的指针。
这里,传入该函数第1个形参const esp_partition_t *partition的实参为handle->update_partition,也就是说将要写入升级数据的分区。再说明白点,就是如果当前运行分区是ota_0,那么update_partition就是ota_1;如果当前运行分区是ota_1,那么update_partition就是ota_0。
而handle->update_partition则是在之前esp_https_ota_begin函数中得到的,相关代码如下:
https_ota_handle->update_partition = NULL;ESP_LOGI(TAG, "Starting OTA...");https_ota_handle->update_partition = esp_ota_get_next_update_partition(NULL);if (https_ota_handle->update_partition == NULL) {ESP_LOGE(TAG, "Passive OTA partition not found");err = ESP_FAIL;goto http_cleanup;}
- is_ota_partition函数
is_ota_partition函数在C:\Espressif\frameworks\esp-idf-v5.2.1\components\app_update\esp_ota_ops.c中,代码如下:
/* Return true if this is an OTA app partition */
static bool is_ota_partition(const esp_partition_t *p)
{return (p != NULL&& p->type == ESP_PARTITION_TYPE_APP&& p->subtype >= ESP_PARTITION_SUBTYPE_APP_OTA_0&& p->subtype < ESP_PARTITION_SUBTYPE_APP_OTA_MAX);
}
is_ota_partition函数很简单,就是检查(给定)分区是否是OTA app分区。
esp_ota_begin函数其余内容的解析请看下回。