Add OTA to the device dashboard. Implement for H5

This commit is contained in:
cpq committed 2023-09-04 22:07:32 +01:00
1 parent d5b5cec797
commit 2f014237b2
19 files changed
+3332 -7534

No files matched your search

+35
View File
@@ -0,0 +1,35 @@
// Copyright (c) 2023 Cesanta Software Limited
// All rights reserved
#pragma once
#include "arch.h"
#define MG_OTA_NONE 0 // No OTA support
#define MG_OTA_STM32H5 1 // STM32 H5 series
#define MG_OTA_CUSTOM 100 // Custom implementation
#define MG_OTA_MAGIC 0xb07afeed
#ifndef MG_OTA
#define MG_OTA MG_OTA_NONE
#endif
// Firmware update API
bool mg_ota_begin(size_t new_firmware_size); // Start writing
bool mg_ota_write(const void *buf, size_t len); // Write firmware chunk
bool mg_ota_end(void); // Stop writing
void mg_sys_reset(void); // Reboot device
struct mg_ota_data {
uint32_t magic; // Must be MG_OTA_MAGIC
uint32_t crc32; // Checksum of the current firmware
uint32_t size; // Firware size
uint32_t time; // Flashing timestamp. Unix epoch, seconds since 1970
uint32_t booted; // -1: not yet booted before, otherwise booted
uint32_t golden; // -1: not yet comitted, otherwise clean, committed
};
bool mg_ota_status(struct mg_ota_data[2]); // Get status for curr and prev fw
bool mg_ota_commit(void); // Commit current firmware
bool mg_ota_rollback(void); // Rollback to prev firmware
+37
View File
@@ -0,0 +1,37 @@
#include "log.h"
#include "ota.h"
#if MG_OTA == MG_OTA_NONE
static struct mg_ota_data s_od[2] = {
{MG_OTA_MAGIC, (uint32_t) -1, (uint32_t) -1, (uint32_t) -1, (uint32_t) -1,
(uint32_t) -1},
{MG_OTA_MAGIC, (uint32_t) -1, (uint32_t) -1, (uint32_t) -1, (uint32_t) -1,
(uint32_t) -1},
};
bool mg_ota_begin(size_t new_firmware_size) {
MG_DEBUG(("Starting firmware update, size %lu", new_firmware_size));
return true;
}
bool mg_ota_write(const void *buf, size_t len) {
MG_DEBUG(("%p %lu", buf, len));
return true;
}
bool mg_ota_end(void) {
return true;
}
bool mg_ota_status(struct mg_ota_data od[2]) {
od[0] = s_od[0];
od[1] = s_od[1];
return true;
}
bool mg_ota_commit(void) {
s_od[0].golden = 0;
return true;
}
bool mg_ota_rollback(void) {
return true;
}
void mg_sys_reset(void) {
MG_DEBUG(("Resetting device..."));
}
#endif
+191
View File
@@ -0,0 +1,191 @@
#include "arch.h"
#include "log.h"
#include "ota.h"
#if MG_OTA == MG_OTA_STM32H5
#define FLASH_BANK1 0x08000000
#define FLASH_SIZE1 0x00100000
#define FLASH_BANK2 0x08100000
#define FLASH_SIZE2 0x00100000
#define FLASH_SSIZE 8192 // Sector (page) size, 8k
// Keep OTA data at the beginning of the last sector of the flash bank
#define FLASH_BANK1_OTA_DATA (FLASH_BANK1 + FLASH_SIZE1 - FLASH_SSIZE)
#define FLASH_BANK2_OTA_DATA (FLASH_BANK2 + FLASH_SIZE2 - FLASH_SSIZE)
#define FLASH_BASE 0x40022000 // Base address of the flash controller
#define FLASH_KEYR (FLASH_BASE + 0x4) // See RM0481 7.11
#define FLASH_OPTKEYR (FLASH_BASE + 0xc)
#define FLASH_OPTCR (FLASH_BASE + 0x1c)
#define FLASH_NSSR (FLASH_BASE + 0x20)
#define FLASH_NSCR (FLASH_BASE + 0x28)
#define FLASH_NSCCR (FLASH_BASE + 0x30)
#define FLASH_OPTSR_CUR (FLASH_BASE + 0x50)
#define FLASH_OPTSR_PRG (FLASH_BASE + 0x54)
#undef REG
#define REG(x) ((volatile uint32_t *) (x))[0]
#undef BIT
#define BIT(x) (((uint32_t) 1U) << (x))
#undef SETBITS
#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK)
static uint32_t s_addr; // Current address to write to
static uint32_t s_crc32; // Firmware checksum
static void flash_unlock(void) {
static bool unlocked = false;
if (unlocked == false) {
REG(FLASH_KEYR) = 0x45670123;
REG(FLASH_KEYR) = 0Xcdef89ab;
REG(FLASH_OPTKEYR) = 0x08192a3b;
REG(FLASH_OPTKEYR) = 0x4c5d6e7f;
unlocked = true;
}
}
static int flash_page_start(volatile uint32_t *dst) {
uint32_t addr = (uint32_t) (uintptr_t) dst;
return (addr & (FLASH_SSIZE - 1)) == 0;
}
static bool flash_is_err(void) {
return REG(FLASH_NSSR) & ((BIT(8) - 1) << 17); // RM0481 7.11.9
}
static void flash_wait(void) {
while ((REG(FLASH_NSSR) & BIT(0)) && (REG(FLASH_NSSR) & BIT(16)) == 0) {
(void) 0;
}
}
static void flash_prep(void) {
flash_wait(); // Wait until ready
REG(FLASH_NSCR) = 0UL; // Clear control reg
REG(FLASH_NSCCR) = ((BIT(9) - 1) << 16U); // Clear all errors
}
static bool flash_bank_is_swapped(void) {
return REG(FLASH_OPTCR) & BIT(31); // RM0481 7.11.8
}
static void flash_erase(uint32_t sector) {
flash_prep();
if ((sector < 128 && flash_bank_is_swapped()) ||
(sector > 127 && !flash_bank_is_swapped())) {
REG(FLASH_NSCR) |= BIT(31); // Set FLASH_CR_BKSEL
}
if (sector > 127) sector -= 128;
REG(FLASH_NSCR) |= BIT(2) | (sector << 6); // SectorErase | sector_num
REG(FLASH_NSCR) |= BIT(5); // Start erasing
// MG_INFO(("ERASE %lu, CR %#lx SR %#lx", sector, REG(FLASH_NSCR),
// REG(FLASH_NSSR)));
flash_prep();
}
static bool flash_swap_bank(void) {
uint32_t desired = flash_bank_is_swapped() ? 0 : BIT(31);
flash_unlock();
flash_prep();
// printf("OPTSR_PRG 1 %#lx\n", FLASH->OPTSR_PRG);
SETBITS(REG(FLASH_OPTSR_PRG), BIT(31), desired);
// printf("OPTSR_PRG 2 %#lx\n", FLASH->OPTSR_PRG);
REG(FLASH_OPTCR) |= BIT(1); // OPTSTART
while ((REG(FLASH_OPTSR_CUR) & BIT(31)) != desired) (void) 0;
return true;
}
static bool flash_write(uint32_t addr, const void *buf, size_t len) {
volatile uint32_t *dst = (uint32_t *) addr;
uint32_t *src = (uint32_t *) buf;
uint32_t *end = (uint32_t *) ((char *) buf + len);
bool success = true;
flash_unlock();
flash_prep();
while (success && src < end) {
uint32_t pageno = ((uint32_t) dst - FLASH_BANK1) / FLASH_SSIZE;
if (flash_page_start(dst)) flash_erase(pageno);
REG(FLASH_NSCR) = BIT(1); // Set programming flag
do {
*dst++ = *src++;
flash_wait();
} while (src < end && !flash_page_start(dst) && !flash_is_err());
#if 0
do {
*dst++ = *src++;
printf("WRITE %p, CR %#lx SR %#lx OCR %#lx %#lx %#lx\n", dst, FLASH->NSCR,
FLASH->NSSR, FLASH->OPTCR, src[-1], dst[-1]);
} while (src < end && !flash_page_start(dst));
#endif
// if (FLASH->NSSR & FLASH_SR_WBNE) FLASH->NSCR |= FLASH_CR_FW;
if (REG(FLASH_NSSR) & BIT(1)) REG(FLASH_NSCR) |= BIT(4);
MG_INFO(("W %p, CR %#lx SR %#lx OCR %#lx %#lx %#lx", dst, REG(FLASH_NSCR),
REG(FLASH_NSSR), REG(FLASH_OPTCR), src[-1], dst[-1]));
flash_wait();
if (flash_is_err() || (src[-1] != dst[-1])) {
// printf(" E %lx CR %#lx SR %#lx, %#lx %#lx\n", (unsigned long) dst,
// FLASH->NSCR, FLASH->NSSR, src[-1], dst[-1]);
success = false;
}
}
return success;
}
void mg_sys_reset(void) {
// SCB->AIRCR = ((0x5fa << SCB_AIRCR_VECTKEY_Pos)|SCB_AIRCR_SYSRESETREQ_Msk);
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
}
bool mg_ota_begin(size_t new_firmware_size) {
s_crc32 = 0;
s_addr = FLASH_BANK2;
MG_DEBUG(("Firmware %lu bytes, max %lu", new_firmware_size, FLASH_SIZE2));
return new_firmware_size < FLASH_SIZE2;
}
bool mg_ota_write(const void *buf, size_t len) {
bool ok = flash_write(s_addr, buf, len); // Write chunk to flash
s_crc32 = mg_crc32(s_crc32, (char *) buf, len); // Update CRC
MG_DEBUG(("%#x %p %lu -> %d", s_addr, buf, len, ok));
s_addr += len;
return ok;
}
static bool w32(uint32_t addr, uint32_t value) {
return flash_write(addr, &value, sizeof(value));
}
bool mg_ota_end(void) {
bool ok = false;
size_t size = s_addr - FLASH_BANK2;
uint32_t crc = mg_crc32(0, (char *) FLASH_BANK2, size);
if (crc == s_crc32) {
uint32_t now = (uint32_t) (mg_now() / 1000);
struct mg_ota_data od = {MG_OTA_MAGIC, crc, size, now, -1, -1};
ok = flash_write(FLASH_BANK2_OTA_DATA, &od, sizeof(od));
}
MG_DEBUG(("CRC check: %x %x", s_crc32, crc));
return ok && flash_swap_bank();
}
bool mg_ota_status(struct mg_ota_data od[2]) {
od[0] = *(struct mg_ota_data *) FLASH_BANK1_OTA_DATA;
od[1] = *(struct mg_ota_data *) FLASH_BANK2_OTA_DATA;
return od[0].magic == MG_OTA_MAGIC;
}
bool mg_ota_commit(void) {
struct mg_ota_data *od = (struct mg_ota_data *) FLASH_BANK1_OTA_DATA;
bool ok = false;
if (od->magic == MG_OTA_MAGIC && od->golden == 0) {
ok = true; // Already clean, do nothing
} else if (od->magic == MG_OTA_MAGIC) { // Dirty!
ok = w32((uint32_t) &od->golden, 0);
}
return ok;
}
bool mg_ota_rollback(void) {
return flash_swap_bank();
}
#endif
+2 -1
View File
@@ -16,7 +16,8 @@ char *mg_random_str(char *buf, size_t len);
uint16_t mg_ntohs(uint16_t net);
uint32_t mg_ntohl(uint32_t net);
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
uint64_t mg_millis(void);
uint64_t mg_millis(void); // Return milliseconds since boot
uint64_t mg_now(void); // Return milliseconds since Epoch
#define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x)