mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
ota ops: Add new functions esp_ota_get_next_update_partition / esp_ota_get_running_partition
* Update OTA example to use these. * Refactor esp_ota_begin() to return ESP_ERR_OTA_PARTITION_CONFLICT as documented
This commit is contained in:
parent
6a2d152086
commit
25f739c183
@ -33,6 +33,7 @@
|
||||
#include "esp_ota_ops.h"
|
||||
#include "rom/queue.h"
|
||||
#include "rom/crc.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
|
||||
@ -75,6 +76,9 @@ esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp
|
||||
if ((partition == NULL) || (out_handle == NULL)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (partition == esp_ota_get_running_partition()) {
|
||||
return ESP_ERR_OTA_PARTITION_CONFLICT;
|
||||
}
|
||||
|
||||
ota_ops_entry_t *new_entry = (ota_ops_entry_t *) calloc(sizeof(ota_ops_entry_t), 1);
|
||||
|
||||
@ -446,3 +450,73 @@ const esp_partition_t *esp_ota_get_boot_partition(void)
|
||||
return esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const esp_partition_t* esp_ota_get_running_partition(void)
|
||||
{
|
||||
/* Find the flash address of this exact function. By definition that is part
|
||||
of the currently running firmware. Then find the enclosing partition. */
|
||||
|
||||
size_t phys_offs = spi_flash_cache2phys(esp_ota_get_running_partition);
|
||||
|
||||
assert (phys_offs != SPI_FLASH_CACHE2PHYS_FAIL); /* indicates cache2phys lookup is buggy */
|
||||
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP,
|
||||
ESP_PARTITION_SUBTYPE_ANY,
|
||||
NULL);
|
||||
assert(it != NULL); /* has to be at least one app partition */
|
||||
|
||||
while (it != NULL) {
|
||||
const esp_partition_t *p = esp_partition_get(it);
|
||||
if (p->address <= phys_offs && p->address + p->size > phys_offs) {
|
||||
esp_partition_iterator_release(it);
|
||||
return p;
|
||||
}
|
||||
it = esp_partition_next(it);
|
||||
}
|
||||
esp_partition_iterator_release(it);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const esp_partition_t* esp_ota_get_next_update_partition(const esp_partition_t *start_from)
|
||||
{
|
||||
const esp_partition_t *result = NULL;
|
||||
bool next_is_result = false;
|
||||
if (start_from == NULL) {
|
||||
start_from = esp_ota_get_running_partition();
|
||||
}
|
||||
assert (start_from != NULL);
|
||||
|
||||
/* Two possibilities: either we want the OTA partition immediately after the
|
||||
current running OTA partition, or we want the first OTA partition we see (for
|
||||
the case when the last OTA partition is the running partition, or if the current
|
||||
running partition is not OTA.)
|
||||
*/
|
||||
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP,
|
||||
ESP_PARTITION_SUBTYPE_ANY,
|
||||
NULL);
|
||||
while (it != NULL) {
|
||||
const esp_partition_t *p = esp_partition_get(it);
|
||||
if(p->subtype >= ESP_PARTITION_SUBTYPE_APP_OTA_0
|
||||
&& p->subtype < ESP_PARTITION_SUBTYPE_APP_OTA_MAX) {
|
||||
/* is OTA partition */
|
||||
if (p == start_from || p->address == start_from->address) {
|
||||
next_is_result = true; /* next OTA partition is the one */
|
||||
}
|
||||
else if (next_is_result) {
|
||||
result = p; /* this is it! */
|
||||
break;
|
||||
}
|
||||
else if (result == NULL) {
|
||||
result = p; /* first OTA partition is the fallback */
|
||||
}
|
||||
}
|
||||
it = esp_partition_next(it);
|
||||
}
|
||||
|
||||
esp_partition_iterator_release(it);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
@ -59,9 +59,10 @@ typedef uint32_t esp_ota_handle_t;
|
||||
|
||||
* @return
|
||||
* - ESP_OK: OTA operation commenced successfully.
|
||||
* - ESP_ERR_INVALID_ARG: partition or out_handle arguments were NULL.
|
||||
* - ESP_ERR_INVALID_ARG: partition or out_handle arguments were NULL, or partition doesn't point to a non OTA app partition.
|
||||
* - ESP_ERR_NO_MEM: Cannot allocate memory for OTA operation.
|
||||
* - ESP_ERR_OTA_PARTITION_CONFLICT: Partition is currently in use, cannot update.
|
||||
* - ESP_ERR_OTA_PARTITION_CONFLICT: Partition holds the currently running firmware, cannot update in place.
|
||||
* - ESP_ERR_NOT_FOUND: Partition argument not found in partition table.
|
||||
* - ESP_ERR_OTA_SELECT_INFO_INVALID: The OTA data partition contains invalid data.
|
||||
* - ESP_ERR_INVALID_SIZE: Partition doesn't fit in configured flash size.
|
||||
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
|
||||
@ -121,12 +122,45 @@ esp_err_t esp_ota_end(esp_ota_handle_t handle);
|
||||
esp_err_t esp_ota_set_boot_partition(const esp_partition_t* partition);
|
||||
|
||||
/**
|
||||
* @brief Get partition info of currently running app
|
||||
* @brief Get partition info of currently configured boot app
|
||||
*
|
||||
* If esp_ota_set_boot_partition() has been called, the partition which was set by that function will be returned.
|
||||
*
|
||||
* If esp_ota_set_boot_partition() has not been called, the result is
|
||||
* equivalent to esp_ota_get_running_partition().
|
||||
*
|
||||
* @return Pointer to info for partition structure, or NULL if no partition is found or flash read operation failed. Returned pointer is valid for the lifetime of the application.
|
||||
*/
|
||||
const esp_partition_t* esp_ota_get_boot_partition(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get partition info of currently running app
|
||||
*
|
||||
* This function is different to esp_ota_get_boot_partition() in that
|
||||
* it ignores any change of selected boot partition caused by
|
||||
* esp_ota_set_boot_partition(). Only the app whose code is currently
|
||||
* running will have its partition information returned.
|
||||
*
|
||||
* @return Pointer to info for partition structure, or NULL if no partition is found or flash read operation failed. Returned pointer is valid for the lifetime of the application.
|
||||
*/
|
||||
const esp_partition_t* esp_ota_get_running_partition(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return the next OTA app partition which should be written with a new firmware.
|
||||
*
|
||||
* Call this function to find an OTA app partition which can be passed to esp_ota_begin().
|
||||
*
|
||||
* Finds next partition round-robin, starting from the current running partition.
|
||||
*
|
||||
* @param start_from If set, treat this partition info as describing the current running partition. Can be NULL, in which case esp_ota_get_running_partition() is used to find the currently running partition. The result of this function is never the same as this argument.
|
||||
*
|
||||
* @return Pointer to info for partition which should be updated next. NULL result indicates invalid OTA data partition, or that no eligible OTA app slot partition was found.
|
||||
*
|
||||
*/
|
||||
const esp_partition_t* esp_ota_get_next_update_partition(const esp_partition_t *start_from);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
5
components/app_update/test/component.mk
Normal file
5
components/app_update/test/component.mk
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
#Component Makefile
|
||||
#
|
||||
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
40
components/app_update/test/test_ota_ops.c
Normal file
40
components/app_update/test/test_ota_ops.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
#include <unity.h>
|
||||
#include <esp_ota_ops.h>
|
||||
|
||||
|
||||
/* These OTA tests currently don't assume an OTA partition exists
|
||||
on the device, so they're a bit limited
|
||||
*/
|
||||
|
||||
TEST_CASE("esp_ota_begin() verifies arguments", "[ota]")
|
||||
{
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
esp_partition_t partition;
|
||||
static esp_ota_handle_t handle = 0;
|
||||
|
||||
if (handle != 0) { /* clean up from any previous test */
|
||||
esp_ota_end(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
/* running partition & configured boot partition are same */
|
||||
TEST_ASSERT_NOT_NULL(running);
|
||||
|
||||
/* trying to 'begin' on running partition fails */
|
||||
TEST_ASSERT_NOT_EQUAL(ESP_OK, esp_ota_begin(running, OTA_SIZE_UNKNOWN, &handle));
|
||||
TEST_ASSERT_EQUAL(0, handle);
|
||||
|
||||
memcpy(&partition, running, sizeof(esp_partition_t));
|
||||
partition.address--;
|
||||
|
||||
/* non existent partition fails */
|
||||
TEST_ASSERT_EQUAL_HEX(ESP_ERR_NOT_FOUND, esp_ota_begin(&partition, OTA_SIZE_UNKNOWN, &handle));
|
||||
TEST_ASSERT_EQUAL(0, handle);
|
||||
}
|
@ -108,6 +108,16 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, bool log_errors, uint32_t *p
|
||||
*p_length = 0;
|
||||
}
|
||||
|
||||
if (src_addr % SPI_FLASH_MMU_PAGE_SIZE != 0) {
|
||||
/* Image must start on a 64KB boundary
|
||||
|
||||
(This is not a technical limitation, only the flash mapped regions need to be 64KB aligned. But the most
|
||||
consistent way to do this is to have all the offsets internal to the image correctly 64KB aligned, and then
|
||||
start the image on a 64KB boundary also.)
|
||||
*/
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
err = esp_image_load_header(src_addr, log_errors, &image_header);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
|
@ -63,7 +63,7 @@ typedef enum {
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13,//!< OTA partition 13
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14,//!< OTA partition 14
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15,//!< OTA partition 15
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_MAX = 15, //!< Max subtype of OTA partition
|
||||
ESP_PARTITION_SUBTYPE_APP_OTA_MAX = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 16,//!< Max subtype of OTA partition
|
||||
ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, //!< Test application partition
|
||||
|
||||
ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition
|
||||
|
@ -71,5 +71,7 @@ Functions
|
||||
.. doxygenfunction:: esp_ota_begin
|
||||
.. doxygenfunction:: esp_ota_write
|
||||
.. doxygenfunction:: esp_ota_end
|
||||
.. doxygenfunction:: esp_ota_get_running_partition
|
||||
.. doxygenfunction:: esp_ota_set_boot_partition
|
||||
.. doxygenfunction:: esp_ota_get_boot_partition
|
||||
.. doxygenfunction:: esp_ota_get_next_update_partition
|
||||
|
@ -33,17 +33,14 @@
|
||||
|
||||
static const char *TAG = "ota";
|
||||
/*an ota data write buffer ready to write to the flash*/
|
||||
char ota_write_data[BUFFSIZE + 1] = { 0 };
|
||||
static char ota_write_data[BUFFSIZE + 1] = { 0 };
|
||||
/*an packet receive buffer*/
|
||||
char text[BUFFSIZE + 1] = { 0 };
|
||||
static char text[BUFFSIZE + 1] = { 0 };
|
||||
/* an image total length*/
|
||||
int binary_file_length = 0;
|
||||
static int binary_file_length = 0;
|
||||
/*socket id*/
|
||||
int socket_id = -1;
|
||||
char http_request[64] = {0};
|
||||
/* operate handle : uninitialized value is zero ,every ota begin would exponential growth*/
|
||||
esp_ota_handle_t out_handle = 0;
|
||||
esp_partition_t operate_partition;
|
||||
static int socket_id = -1;
|
||||
static char http_request[64] = {0};
|
||||
|
||||
/* FreeRTOS event group to signal when we are connected & ready to make a request */
|
||||
static EventGroupHandle_t wifi_event_group;
|
||||
@ -109,7 +106,7 @@ static int read_until(char *buffer, char delim, int len)
|
||||
* return true if packet including \r\n\r\n that means http packet header finished,start to receive packet body
|
||||
* otherwise return false
|
||||
* */
|
||||
static bool read_past_http_header(char text[], int total_len, esp_ota_handle_t out_handle)
|
||||
static bool read_past_http_header(char text[], int total_len, esp_ota_handle_t update_handle)
|
||||
{
|
||||
/* i means current position */
|
||||
int i = 0, i_read_len = 0;
|
||||
@ -122,7 +119,7 @@ static bool read_past_http_header(char text[], int total_len, esp_ota_handle_t o
|
||||
/*copy first http packet body to write buffer*/
|
||||
memcpy(ota_write_data, &(text[i + 2]), i_write_len);
|
||||
|
||||
esp_err_t err = esp_ota_write( out_handle, (const void *)ota_write_data, i_write_len);
|
||||
esp_err_t err = esp_ota_write( update_handle, (const void *)ota_write_data, i_write_len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error: esp_ota_write failed! err=0x%x", err);
|
||||
return false;
|
||||
@ -170,48 +167,6 @@ bool connect_to_http_server()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ota_init()
|
||||
{
|
||||
esp_err_t err;
|
||||
const esp_partition_t *esp_current_partition = esp_ota_get_boot_partition();
|
||||
if (esp_current_partition->type != ESP_PARTITION_TYPE_APP) {
|
||||
ESP_LOGE(TAG, "Error: esp_current_partition->type != ESP_PARTITION_TYPE_APP");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_partition_t find_partition;
|
||||
memset(&operate_partition, 0, sizeof(esp_partition_t));
|
||||
/*choose which OTA image should we write to*/
|
||||
switch (esp_current_partition->subtype) {
|
||||
case ESP_PARTITION_SUBTYPE_APP_FACTORY:
|
||||
find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0;
|
||||
break;
|
||||
case ESP_PARTITION_SUBTYPE_APP_OTA_0:
|
||||
find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_1;
|
||||
break;
|
||||
case ESP_PARTITION_SUBTYPE_APP_OTA_1:
|
||||
find_partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
find_partition.type = ESP_PARTITION_TYPE_APP;
|
||||
|
||||
const esp_partition_t *partition = esp_partition_find_first(find_partition.type, find_partition.subtype, NULL);
|
||||
assert(partition != NULL);
|
||||
memset(&operate_partition, 0, sizeof(esp_partition_t));
|
||||
err = esp_ota_begin( partition, OTA_SIZE_UNKNOWN, &out_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ota_begin failed err=0x%x!", err);
|
||||
return false;
|
||||
} else {
|
||||
memcpy(&operate_partition, partition, sizeof(esp_partition_t));
|
||||
ESP_LOGI(TAG, "esp_ota_begin init OK");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) task_fatal_error()
|
||||
{
|
||||
ESP_LOGE(TAG, "Exiting task due to fatal error...");
|
||||
@ -226,7 +181,19 @@ void __attribute__((noreturn)) task_fatal_error()
|
||||
void main_task(void *pvParameter)
|
||||
{
|
||||
esp_err_t err;
|
||||
/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
|
||||
esp_ota_handle_t update_handle = 0 ;
|
||||
const esp_partition_t *update_partition = NULL;
|
||||
|
||||
ESP_LOGI(TAG, "Starting OTA example...");
|
||||
|
||||
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
|
||||
assert(configured == running); /* fresh from reset, should be running from configured boot partition */
|
||||
ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
|
||||
configured->type, configured->subtype, configured->address);
|
||||
|
||||
/* Wait for the callback to set the CONNECTED_BIT in the
|
||||
event group.
|
||||
*/
|
||||
@ -252,12 +219,17 @@ void main_task(void *pvParameter)
|
||||
ESP_LOGI(TAG, "Send GET request to server succeeded");
|
||||
}
|
||||
|
||||
if ( ota_init() ) {
|
||||
ESP_LOGI(TAG, "OTA Init succeeded");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "OTA Init failed");
|
||||
update_partition = esp_ota_get_next_update_partition(NULL);
|
||||
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
|
||||
update_partition->subtype, update_partition->address);
|
||||
assert(update_partition != NULL);
|
||||
|
||||
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ota_begin failed, error=%d", err);
|
||||
task_fatal_error();
|
||||
}
|
||||
ESP_LOGI(TAG, "esp_ota_begin succeeded");
|
||||
|
||||
bool resp_body_start = false, flag = true;
|
||||
/*deal with all receive packet*/
|
||||
@ -270,10 +242,10 @@ void main_task(void *pvParameter)
|
||||
task_fatal_error();
|
||||
} else if (buff_len > 0 && !resp_body_start) { /*deal with response header*/
|
||||
memcpy(ota_write_data, text, buff_len);
|
||||
resp_body_start = read_past_http_header(text, buff_len, out_handle);
|
||||
resp_body_start = read_past_http_header(text, buff_len, update_handle);
|
||||
} else if (buff_len > 0 && resp_body_start) { /*deal with response body*/
|
||||
memcpy(ota_write_data, text, buff_len);
|
||||
err = esp_ota_write( out_handle, (const void *)ota_write_data, buff_len);
|
||||
err = esp_ota_write( update_handle, (const void *)ota_write_data, buff_len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error: esp_ota_write failed! err=0x%x", err);
|
||||
task_fatal_error();
|
||||
@ -291,11 +263,11 @@ void main_task(void *pvParameter)
|
||||
|
||||
ESP_LOGI(TAG, "Total Write binary data length : %d", binary_file_length);
|
||||
|
||||
if (esp_ota_end(out_handle) != ESP_OK) {
|
||||
if (esp_ota_end(update_handle) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ota_end failed!");
|
||||
task_fatal_error();
|
||||
}
|
||||
err = esp_ota_set_boot_partition(&operate_partition);
|
||||
err = esp_ota_set_boot_partition(update_partition);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed! err=0x%x", err);
|
||||
task_fatal_error();
|
Loading…
x
Reference in New Issue
Block a user