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:
Angus Gratton 2017-02-20 14:02:39 +11:00
parent 6a2d152086
commit 25f739c183
8 changed files with 201 additions and 64 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -0,0 +1,5 @@
#
#Component Makefile
#
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

View 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);
}

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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();