Files
esp-idf/examples/system/ota/partitions_ota/main/partition_utils.c
T
Konstantin Kondrashov 97d150d69a feat(app_update): OTA update bootloader, partition_table and other partitions
Passive app partition can be used as the staging partition where a new image is loaded.
Then copy it to the final partition.

Closes: https://github.com/espressif/esp-idf/issues/14195
Closes: https://github.com/espressif/esp-idf/issues/13824
2024-11-07 13:48:51 +02:00

119 lines
4.2 KiB
C

/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <sys/param.h>
#include "esp_flash_partitions.h"
#include "esp_flash_internal.h"
#include "esp_image_format.h"
#include "esp_flash.h"
#include "esp_partition.h"
#include "esp_log.h"
// Returns the number of partitions belonging to the specified flash chip
static unsigned int count_partitions(esp_flash_t *flash_chip)
{
unsigned int partition_count = 0;
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
assert(it != NULL);
for (; it != NULL; it = esp_partition_next(it)) {
if (esp_partition_get(it)->flash_chip == flash_chip) {
partition_count++;
}
}
esp_partition_iterator_release(it);
return partition_count;
}
// Compare function for qsort to sort partitions by address
static int compare_partitions(const void *a, const void *b) {
const esp_partition_t *part_a = *(const esp_partition_t **)a;
const esp_partition_t *part_b = *(const esp_partition_t **)b;
return ((int32_t)part_a->address - (int32_t)part_b->address);
}
static const esp_partition_t **get_sorted_partition_array(esp_flash_t *flash_chip, unsigned int *out_partition_count)
{
// Number of partitions on the flash chip
unsigned int partition_count = count_partitions(flash_chip);
assert(partition_count != 0);
*out_partition_count = partition_count;
// Allocate an array for copying partitions
const esp_partition_t **part_array = (const esp_partition_t **) calloc(partition_count, sizeof(esp_partition_t *));
assert(part_array != NULL);
// Copy partitions to the array
unsigned i = 0;
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
assert(it != NULL);
for (; it != NULL; it = esp_partition_next(it)) {
const esp_partition_t *p = esp_partition_get(it);
if (p->flash_chip == flash_chip) {
part_array[i++] = p;
}
}
esp_partition_iterator_release(it);
assert(i == partition_count);
// Sort partitions by address using qsort
qsort(part_array, partition_count, sizeof(esp_partition_t *), compare_partitions);
return part_array;
}
esp_err_t partition_utils_find_unallocated(esp_flash_t *flash_chip, size_t required_size, uint32_t start_offset, uint32_t *found_offset, size_t *found_size)
{
if (flash_chip == NULL) {
flash_chip = esp_flash_default_chip;
}
if (required_size == 0 || found_offset == NULL) {
return ESP_ERR_INVALID_ARG;
}
// Get a sorted partition array for the given flash chip
unsigned int partition_count;
const esp_partition_t **sorted_partitions = get_sorted_partition_array(flash_chip, &partition_count);
if (sorted_partitions == NULL) {
return ESP_ERR_NO_MEM;
}
// Scan for unallocated space between partitions, starting after the start_offset with unused space >= required_size
uint32_t last_end_address = start_offset;
for (size_t i = 0; i < partition_count; ++i) {
if (sorted_partitions[i]->address > start_offset && sorted_partitions[i]->address > last_end_address) {
size_t unallocated_size = sorted_partitions[i]->address - last_end_address;
if (unallocated_size >= required_size) {
*found_offset = last_end_address;
if (found_size != NULL) {
*found_size = unallocated_size;
}
free(sorted_partitions);
return ESP_OK;
}
}
last_end_address = sorted_partitions[i]->address + sorted_partitions[i]->size;
}
// Check for unallocated space after the last partition
last_end_address = MAX(last_end_address, start_offset);
size_t unallocated_size = flash_chip->size - last_end_address;
if (unallocated_size >= required_size) {
*found_offset = last_end_address;
if (found_size != NULL) {
*found_size = unallocated_size;
}
free(sorted_partitions);
return ESP_OK;
}
free(sorted_partitions);
return ESP_ERR_NOT_FOUND;
}