Преглед изворни кода

Merge branch 'feature/app_update_add_api' into 'master'

Add API to write data in a non-contiguous manner

See merge request espressif/esp-idf!8650
Mahavir Jain пре 5 година
родитељ
комит
e8035c3eff

+ 37 - 0
components/app_update/esp_ota_ops.c

@@ -250,6 +250,43 @@ esp_err_t esp_ota_write(esp_ota_handle_t handle, const void *data, size_t size)
     return ESP_ERR_INVALID_ARG;
 }
 
+esp_err_t esp_ota_write_with_offset(esp_ota_handle_t handle, const void *data, size_t size, uint32_t offset)
+{
+    const uint8_t *data_bytes = (const uint8_t *)data;
+    esp_err_t ret;
+    ota_ops_entry_t *it;
+
+    if (data == NULL) {
+        ESP_LOGE(TAG, "write data is invalid");
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    // find ota handle in linked list
+    for (it = LIST_FIRST(&s_ota_ops_entries_head); it != NULL; it = LIST_NEXT(it, entries)) {
+        if (it->handle == handle) {
+            // must erase the partition before writing to it
+            assert(it->erased_size > 0 && "must erase the partition before writing to it");
+
+            /* esp_ota_write_with_offset is used to write data in non contiguous manner.
+             * Hence, unaligned data(less than 16 bytes) cannot be cached if flash encryption is enabled.
+             */
+            if (esp_flash_encryption_enabled() && (size % 16)) {
+                ESP_LOGE(TAG, "Size should be 16byte aligned for flash encryption case");
+                return ESP_ERR_INVALID_ARG;
+            }
+            ret = esp_partition_write(it->part, offset, data_bytes, size);
+            if (ret == ESP_OK) {
+                it->wrote_size += size;
+            }
+            return ret;
+        }
+    }
+
+    // OTA handle is not found in linked list
+    ESP_LOGE(TAG,"OTA handle not found");
+    return ESP_ERR_INVALID_ARG;
+}
+
 esp_err_t esp_ota_end(esp_ota_handle_t handle)
 {
     ota_ops_entry_t *it;

+ 23 - 0
components/app_update/include/esp_ota_ops.h

@@ -117,6 +117,29 @@ esp_err_t esp_ota_begin(const esp_partition_t* partition, size_t image_size, esp
  */
 esp_err_t esp_ota_write(esp_ota_handle_t handle, const void* data, size_t size);
 
+/**
+ * @brief   Write OTA update data to partition
+ *
+ * This function can write data in non contiguous manner.
+ * If flash encryption is enabled, data should be 16 byte aligned.
+ *
+ * @param handle  Handle obtained from esp_ota_begin
+ * @param data    Data buffer to write
+ * @param size    Size of data buffer in bytes
+ * @param offset  Offset in flash partition
+ *
+ * @note While performing OTA, if the packets arrive out of order, esp_ota_write_with_offset() can be used to write data in non contiguous manner.
+ *       Use of esp_ota_write_with_offset() in combination with esp_ota_write() is not recommended.
+ *
+ * @return
+ *    - ESP_OK: Data was written to flash successfully.
+ *    - ESP_ERR_INVALID_ARG: handle is invalid.
+ *    - ESP_ERR_OTA_VALIDATE_FAILED: First byte of image contains invalid app image magic byte.
+ *    - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
+ *    - ESP_ERR_OTA_SELECT_INFO_INVALID: OTA data partition has invalid contents
+ */
+esp_err_t esp_ota_write_with_offset(esp_ota_handle_t handle, const void *data, size_t size, uint32_t offset);
+
 /**
  * @brief Finish OTA update and validate newly written app image.
  *

+ 82 - 0
components/app_update/test/test_switch_ota.c

@@ -55,6 +55,29 @@ static void copy_app_partition(esp_ota_handle_t update_handle, const esp_partiti
     ESP_LOGI(TAG, "finish the copy process");
 }
 
+/* @brief Copies a current app to next partition using handle.
+ *
+ * @param[in] update_handle - Handle of API ota.
+ * @param[in] cur_app - Current app.
+ */
+static void copy_app_partition_with_offset(esp_ota_handle_t update_handle, const esp_partition_t *curr_app)
+{
+    const void *partition_bin = NULL;
+    spi_flash_mmap_handle_t  data_map;
+    ESP_LOGI(TAG, "start the copy process");
+    uint32_t offset = 0, bytes_to_write = curr_app->size;
+    uint32_t write_bytes;
+    while (bytes_to_write > 0) {
+        write_bytes = (bytes_to_write > (4 * 1024)) ? (4 * 1024) : bytes_to_write;
+        TEST_ESP_OK(esp_partition_mmap(curr_app, offset, write_bytes, SPI_FLASH_MMAP_DATA, &partition_bin, &data_map));
+        TEST_ESP_OK(esp_ota_write_with_offset(update_handle, (const void *)partition_bin, write_bytes, offset));
+        spi_flash_munmap(data_map);
+        bytes_to_write -= write_bytes;
+        offset += write_bytes;
+    }
+    ESP_LOGI(TAG, "finish the copy process");
+}
+
 #if defined(CONFIG_BOOTLOADER_FACTORY_RESET) || defined(CONFIG_BOOTLOADER_APP_TEST)
 /* @brief Copies partition from source partition to destination partition.
  *
@@ -105,6 +128,26 @@ static void copy_current_app_to_next_part(const esp_partition_t *cur_app_partiti
     TEST_ESP_OK(esp_ota_set_boot_partition(next_app_partition));
 }
 
+/* @brief Copies a current app to next partition (OTA0-15) and then configure OTA data for a new boot partition.
+ *
+ * @param[in] cur_app_partition - Current app.
+ * @param[in] next_app_partition - Next app for boot.
+ */
+static void copy_current_app_to_next_part_with_offset(const esp_partition_t *cur_app_partition, const esp_partition_t *next_app_partition)
+{
+    esp_ota_get_next_update_partition(NULL);
+    TEST_ASSERT_NOT_EQUAL(NULL, next_app_partition);
+    ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", next_app_partition->subtype, next_app_partition->address);
+
+    esp_ota_handle_t update_handle = 0;
+    TEST_ESP_OK(esp_ota_begin(next_app_partition, OTA_SIZE_UNKNOWN, &update_handle));
+
+    copy_app_partition_with_offset(update_handle, cur_app_partition);
+
+    TEST_ESP_OK(esp_ota_end(update_handle));
+    TEST_ESP_OK(esp_ota_set_boot_partition(next_app_partition));
+}
+
 /* @brief Erase otadata partition
  */
 static void erase_ota_data(void)
@@ -133,6 +176,16 @@ static void copy_current_app_to_next_part_and_reboot(void)
     reboot_as_deep_sleep();
 }
 
+/* @brief Copies a current app to next partition (OTA0-15) using esp_ota_write_with_offest(), after that ESP is rebooting and run this (the next) OTAx.
+ */
+static void copy_current_app_to_next_part_with_offset_and_reboot(void)
+{
+    const esp_partition_t *cur_app = esp_ota_get_running_partition();
+    ESP_LOGI(TAG, "copy current app to next part");
+    copy_current_app_to_next_part_with_offset(cur_app, get_next_update_partition());
+    reboot_as_deep_sleep();
+}
+
 /* @brief Get running app.
  *
  * @return The next partition of OTA(OTA0-15).
@@ -740,3 +793,32 @@ static void test_erase_last_app_rollback(void)
 // 4 Stage: run OTA1           -> check it -> erase OTA0 and rollback                          -> reboot
 // 5 Stage: run factory        -> check it -> erase OTA_DATA for next tests                    -> PASS
 TEST_CASE_MULTIPLE_STAGES("Test erase_last_boot_app_partition. factory, OTA1, OTA0, factory", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET, DEEPSLEEP_RESET, SW_CPU_RESET]", start_test, test_erase_last_app_flow, test_erase_last_app_flow, test_erase_last_app_flow, test_erase_last_app_rollback);
+
+static void test_flow6(void)
+{
+    boot_count++;
+    ESP_LOGI(TAG, "boot count %d", boot_count);
+    const esp_partition_t *cur_app = get_running_firmware();
+    switch (boot_count) {
+        case 2:
+            ESP_LOGI(TAG, "Factory");
+            TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, cur_app->subtype);
+            copy_current_app_to_next_part_with_offset_and_reboot();
+            break;
+        case 3:
+            ESP_LOGI(TAG, "OTA0");
+            TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_OTA_0, cur_app->subtype);
+            mark_app_valid();
+            erase_ota_data();
+            break;
+        default:
+            erase_ota_data();
+            TEST_FAIL_MESSAGE("Unexpected stage");
+            break;
+    }
+}
+
+// 1 Stage: After POWER_RESET erase OTA_DATA for this test              -> reboot through deep sleep.
+// 2 Stage: run factory -> check it -> copy factory to OTA0             -> reboot  --//--
+// 3 Stage: run OTA0    -> check it -> erase OTA_DATA for next tests    -> PASS
+TEST_CASE_MULTIPLE_STAGES("Switching between factory, OTA0 using esp_ota_write_with_offset", "[app_update][timeout=90][reset=DEEPSLEEP_RESET, DEEPSLEEP_RESET]", start_test, test_flow6, test_flow6);