[core] Migrate to uf2ota v5.0.0, refactor OTA API
This commit is contained in:
@@ -2,10 +2,148 @@
|
||||
|
||||
#include "lt_ota.h"
|
||||
|
||||
#include <uf2ota/uf2ota.h>
|
||||
|
||||
#define UF2_CTX_SIZE (sizeof(uf2_ota_t) + sizeof(uf2_info_t))
|
||||
|
||||
static inline size_t lt_ota_buf_left(lt_ota_ctx_t *ctx) {
|
||||
return ctx->buf + UF2_BLOCK_SIZE - ctx->buf_pos;
|
||||
}
|
||||
|
||||
static inline size_t lt_ota_buf_size(lt_ota_ctx_t *ctx) {
|
||||
return ctx->buf_pos - ctx->buf;
|
||||
}
|
||||
|
||||
void lt_ota_begin(lt_ota_ctx_t *ctx, size_t size) {
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
memset((void *)ctx + UF2_CTX_SIZE, 0, sizeof(lt_ota_ctx_t) - UF2_CTX_SIZE);
|
||||
uf2_ctx_init(&ctx->uf2, lt_ota_get_uf2_scheme(), lt_cpu_get_family());
|
||||
uf2_info_init(&ctx->info);
|
||||
ctx->buf_pos = ctx->buf;
|
||||
ctx->bytes_total = size;
|
||||
ctx->running = true;
|
||||
|
||||
lt_ota_set_write_protect(&ctx->uf2);
|
||||
|
||||
LT_DM(OTA, "begin(%u, ...) / OTA curr: %u, scheme: %u", size, lt_ota_dual_get_current(), lt_ota_get_uf2_scheme());
|
||||
}
|
||||
|
||||
bool lt_ota_end(lt_ota_ctx_t *ctx) {
|
||||
if (!ctx || !ctx->running)
|
||||
return true;
|
||||
|
||||
uf2_ctx_free(&ctx->uf2);
|
||||
uf2_info_free(&ctx->info);
|
||||
ctx->running = false;
|
||||
|
||||
if (ctx->bytes_written && ctx->bytes_written == ctx->bytes_total) {
|
||||
// try to activate the 2nd image
|
||||
return lt_ota_switch(/* revert= */ false);
|
||||
}
|
||||
|
||||
// activation not attempted (update aborted)
|
||||
return true;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void lt_ota_set_write_protect(uf2_ota_t *uf2) {}
|
||||
|
||||
size_t lt_ota_write(lt_ota_ctx_t *ctx, const uint8_t *data, size_t len) {
|
||||
if (!ctx || !ctx->running)
|
||||
return 0;
|
||||
|
||||
// write until buffer space is available
|
||||
size_t written = 0;
|
||||
uint16_t to_write; // 1..512
|
||||
while (len && (to_write = MIN((uint16_t)len, lt_ota_buf_left(ctx)))) {
|
||||
LT_VM(OTA, "Writing %u to buffer (%u/512)", len, lt_ota_buf_size(ctx));
|
||||
|
||||
uf2_block_t *block = NULL;
|
||||
if (to_write == UF2_BLOCK_SIZE) {
|
||||
// data has a complete block; don't use the buffer
|
||||
block = (uf2_block_t *)data;
|
||||
} else {
|
||||
// data has a part of a block; append it to the buffer
|
||||
memcpy(ctx->buf_pos, data, to_write);
|
||||
ctx->buf_pos += to_write;
|
||||
if (lt_ota_buf_size(ctx) == UF2_BLOCK_SIZE) {
|
||||
// the block is complete now
|
||||
block = (uf2_block_t *)ctx->buf;
|
||||
}
|
||||
}
|
||||
|
||||
// write if a block is ready
|
||||
if (block && lt_ota_write_block(ctx, block) == false)
|
||||
// return on errors
|
||||
return written;
|
||||
data += to_write;
|
||||
len -= to_write;
|
||||
written += to_write;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
bool lt_ota_write_block(lt_ota_ctx_t *ctx, uf2_block_t *block) {
|
||||
ctx->error = uf2_check_block(&ctx->uf2, block);
|
||||
if (ctx->error > UF2_ERR_IGNORE)
|
||||
// block is invalid
|
||||
return false;
|
||||
|
||||
if (!ctx->bytes_written) {
|
||||
// parse header block to allow retrieving firmware info
|
||||
ctx->error = uf2_parse_header(&ctx->uf2, block, &ctx->info);
|
||||
if (ctx->error != UF2_ERR_OK)
|
||||
return false;
|
||||
|
||||
LT_IM(
|
||||
OTA,
|
||||
"%s v%s - LT v%s @ %s",
|
||||
ctx->info.fw_name,
|
||||
ctx->info.fw_version,
|
||||
ctx->info.lt_version,
|
||||
ctx->info.board
|
||||
);
|
||||
|
||||
if (ctx->bytes_total == 0) {
|
||||
// set total update size from block count info
|
||||
ctx->bytes_total = block->block_count * UF2_BLOCK_SIZE;
|
||||
} else if (ctx->bytes_total != block->block_count * UF2_BLOCK_SIZE) {
|
||||
// given update size does not match the block count
|
||||
LT_EM(
|
||||
OTA,
|
||||
"Image size wrong; got %u, calculated %llu",
|
||||
ctx->bytes_total,
|
||||
block->block_count * UF2_BLOCK_SIZE
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} else if (ctx->error == UF2_ERR_OK) {
|
||||
// write data blocks normally
|
||||
ctx->error = uf2_write(&ctx->uf2, block);
|
||||
if (ctx->error > UF2_ERR_IGNORE)
|
||||
// block writing failed
|
||||
return false;
|
||||
}
|
||||
|
||||
// increment total writing progress
|
||||
ctx->bytes_written += UF2_BLOCK_SIZE;
|
||||
// call progress callback
|
||||
if (ctx->callback)
|
||||
ctx->callback(ctx->callback_param);
|
||||
// reset the buffer as it's used already
|
||||
if (lt_ota_buf_size(ctx) == UF2_BLOCK_SIZE)
|
||||
ctx->buf_pos = ctx->buf;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lt_ota_can_rollback() {
|
||||
if (lt_ota_get_type() != OTA_TYPE_DUAL)
|
||||
return false;
|
||||
uint8_t current = lt_ota_dual_get_current();
|
||||
if (current == 0)
|
||||
return false;
|
||||
return lt_ota_is_valid(current ^ 0b11);
|
||||
}
|
||||
|
||||
@@ -13,6 +151,8 @@ uf2_ota_scheme_t lt_ota_get_uf2_scheme() {
|
||||
if (lt_ota_get_type() == OTA_TYPE_SINGLE)
|
||||
return UF2_SCHEME_DEVICE_SINGLE;
|
||||
uint8_t current = lt_ota_dual_get_current();
|
||||
if (current == 0)
|
||||
return UF2_SCHEME_DEVICE_DUAL_1;
|
||||
// UF2_SCHEME_DEVICE_DUAL_1 or UF2_SCHEME_DEVICE_DUAL_2
|
||||
return (uf2_ota_scheme_t)(current ^ 0b11);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,72 @@ typedef enum {
|
||||
OTA_TYPE_FILE = 2,
|
||||
} lt_ota_type_t;
|
||||
|
||||
/**
|
||||
* @brief OTA update process context.
|
||||
*/
|
||||
typedef struct {
|
||||
uf2_ota_t uf2;
|
||||
uf2_info_t info;
|
||||
uint8_t buf[UF2_BLOCK_SIZE]; // block data buffer
|
||||
uint8_t *buf_pos; // buffer writing position
|
||||
uint32_t bytes_written; // update progress
|
||||
uint32_t bytes_total; // total update size
|
||||
uf2_err_t error; // LT OTA/uf2ota error code
|
||||
bool running; // whether update has begun
|
||||
void (*callback)(void *param); // progress callback
|
||||
void *callback_param; // callback argument
|
||||
} lt_ota_ctx_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the update context to begin OTA process.
|
||||
*
|
||||
* @param ctx OTA context
|
||||
* @param size length of the update file; 0 if unknown
|
||||
*/
|
||||
void lt_ota_begin(lt_ota_ctx_t *ctx, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Finish the update process. If the update has been written completely,
|
||||
* try to activate the target image. Free allocated internal structures, regardless
|
||||
* of the activation result.
|
||||
*
|
||||
* @param ctx OTA context
|
||||
* @return false if activation was attempted and not successful; true otherwise
|
||||
*/
|
||||
bool lt_ota_end(lt_ota_ctx_t *ctx);
|
||||
|
||||
/**
|
||||
* @brief Set family-specific, write-protected flash areas in the OTA update context.
|
||||
* This shouldn't be called manually, as it's done by lt_ota_begin().
|
||||
*
|
||||
* @param uf2 uf2ota context
|
||||
*/
|
||||
void lt_ota_set_write_protect(uf2_ota_t *uf2);
|
||||
|
||||
/**
|
||||
* @brief Process a chunk of data.
|
||||
*
|
||||
* Data is written to the buffer, unless a full UF2 block is already available,
|
||||
* in which case it's also processed by UF2OTA and written to flash.
|
||||
*
|
||||
* It's advised to write in 512-byte chunks (or its multiples).
|
||||
*
|
||||
* @param ctx OTA context
|
||||
* @param data chunk of bytes to process
|
||||
* @param len size of the chunk
|
||||
* @return number of bytes correctly processed; should equal 'len' in case of no errors
|
||||
*/
|
||||
size_t lt_ota_write(lt_ota_ctx_t *ctx, const uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Try to write the block. In case of UF2 errors, error code is set in the context.
|
||||
* Note: use lt_ota_write() instead. This is for internal usage only.
|
||||
*
|
||||
* @param block UF2 block to check and write; cannot be NULL
|
||||
* @return whether no error has occurred
|
||||
*/
|
||||
bool lt_ota_write_block(lt_ota_ctx_t *ctx, uf2_block_t *block);
|
||||
|
||||
/**
|
||||
* @brief Get OTA type of the device's chip.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-28. */
|
||||
|
||||
#include "lt_logger.h"
|
||||
|
||||
#if __has_include(<sdk_private.h>)
|
||||
#include <sdk_private.h>
|
||||
#endif
|
||||
|
||||
#if LT_HAS_PRINTF
|
||||
#include <printf/printf.h>
|
||||
@@ -33,7 +36,11 @@
|
||||
#define COLOR_BRIGHT_CYAN 0x16
|
||||
#define COLOR_BRIGHT_WHITE 0x17
|
||||
|
||||
static uint32_t uart_port = LT_UART_DEFAULT_LOGGER;
|
||||
#ifdef LT_UART_DEFAULT_PORT
|
||||
static uint32_t uart_port = LT_UART_DEFAULT_LOGGER;
|
||||
#else
|
||||
static uint32_t uart_port = 0;
|
||||
#endif
|
||||
static const char levels[] = {'V', 'D', 'I', 'W', 'E', 'F'};
|
||||
|
||||
#if LT_LOGGER_COLOR
|
||||
|
||||
Reference in New Issue
Block a user