diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c b/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c index 3fd164e5152..f3a18338085 100644 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c @@ -22,6 +22,7 @@ void mg_random(void *buf, size_t len) { // Use on-board RNG memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r)); } } + static void timer_fn(void *arg) { gpio_toggle(LED); // Blink LED struct mg_tcpip_if *ifp = arg; // And show @@ -32,15 +33,42 @@ static void timer_fn(void *arg) { } int main(void) { - gpio_output(LED); // Setup blue LED + gpio_output(LED); // Setup green LED uart_init(UART_DEBUG, 115200); // Initialise debug printf ethernet_init(); // Initialise ethernet pins + + #ifdef MQTT_DASHBOARD + // User can customise the MQTT url, device ID or the root topic below + #define DEVICE_ID "RT1060" + g_url = MQTT_SERVER_URL; + g_device_id = DEVICE_ID; + g_root_topic = DEFAULT_ROOT_TOPIC; + #endif + MG_INFO(("Starting, CPU freq %g MHz", (double) SystemCoreClock / 1000000)); struct mg_mgr mgr; // Initialise mg_mgr_init(&mgr); // Mongoose event manager mg_log_set(MG_LL_DEBUG); // Set log level + //mg_ota_boot(); // Call bootloader: continue to load, or boot another FW + +#if MG_OTA == MG_OTA_FLASH + // Demonstrate the use of mg_flash_{load/save} functions for keeping device + // configuration data on flash. Increment boot count on every boot. + struct deviceconfig { + uint32_t boot_count; + char some_other_data[40]; + }; + uint32_t key = 0x12345678; // A unique key, one per data type + struct deviceconfig dc = {}; // Initialise to some default values + mg_flash_load(NULL, key, &dc, sizeof(dc)); // Load from flash + dc.boot_count++; // Increment boot count + mg_flash_save(NULL, key, &dc, sizeof(dc)); // And save back + MG_INFO(("Boot count: %u", dc.boot_count)); +#endif + + // Initialise Mongoose network stack // Initialise Mongoose network stack struct mg_tcpip_driver_imxrt_data driver_data = {.mdc_cr = 24, .phy_addr = 2}; struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(), @@ -65,6 +93,6 @@ int main(void) { for (;;) { mg_mgr_poll(&mgr, 0); } - return 0; } + diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h b/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h index 450587c69a2..b7584012abe 100644 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h @@ -2,6 +2,8 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_NEWLIB +#define MG_OTA MG_OTA_EXTERNAL_FLASH +#define MG_DEVICE MG_DEVICE_RT1060_QUAD_NOR #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_DRIVER_IMXRT 1 diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/net.c b/examples/nxp/rt1060-evk-make-baremetal-builtin/net.c index fe0e6f06e7b..39be7659aca 120000 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/net.c +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/net.c @@ -1 +1 @@ -../../device-dashboard/net.c \ No newline at end of file +../../mqtt-dashboard/device/net.c \ No newline at end of file diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/net.h b/examples/nxp/rt1060-evk-make-baremetal-builtin/net.h index 9de896ef4e3..cb6bd99fed3 120000 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/net.h +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/net.h @@ -1 +1 @@ -../../device-dashboard/net.h \ No newline at end of file +../../mqtt-dashboard/device/net.h \ No newline at end of file diff --git a/mongoose.c b/mongoose.c index 9be9e5a1103..c1b04e671c4 100644 --- a/mongoose.c +++ b/mongoose.c @@ -323,6 +323,201 @@ bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) { } #endif +#ifdef MG_ENABLE_LINES +#line 1 "src/device_rt1060_quad_nor.c" +#endif + + + +#if MG_DEVICE == MG_DEVICE_RT1060_QUAD_NOR + +typedef struct { + char memCfg[448]; + uint32_t pageSize; + uint32_t sectorSize; + uint32_t ipCmdSerialClkFreq; + char reserved[55]; +} flexspi_nor_config_t; + +typedef struct _serial_nor_config_option +{ + union + { + struct + { + uint32_t max_freq : 4; //!< Maximum supported Frequency + uint32_t misc_mode : 4; //!< miscellaneous mode + uint32_t quad_mode_setting : 4; //!< Quad mode setting + uint32_t cmd_pads : 4; //!< Command pads + uint32_t query_pads : 4; //!< SFDP read pads + uint32_t device_type : 4; //!< Device type + uint32_t option_size : 4; //!< Option size, in terms of uint32_t, size = (option_size + 1) * 4 + uint32_t tag : 4; //!< Tag, must be 0x0E + } B; + uint32_t U; + } option0; + union + { + struct + { + uint32_t dummy_cycles : 8; //!< Dummy cycles before read + uint32_t reserved0 : 8; //!< Reserved for future use + uint32_t pinmux_group : 4; //!< The pinmux group selection + uint32_t reserved1 : 8; //!< Reserved for future use + uint32_t flash_connection : 4; //!< Flash connection option: 0 - Single Flash connected to port A + } B; + uint32_t U; + } option1; +} serial_nor_config_option_t; + +typedef int status_t; + +typedef enum _FlexSPIOperationType +{ + kFlexSpiOperation_Command, //!< FlexSPI operation: Only command, both TX and + //! RX buffer are ignored. + kFlexSpiOperation_Config, //!< FlexSPI operation: Configure device mode, the + //! TX FIFO size is fixed in LUT. + kFlexSpiOperation_Write, //!< FlexSPI operation: Write, only TX buffer is + //! effective + kFlexSpiOperation_Read, //!< FlexSPI operation: Read, only Rx Buffer is + kFlexSpiOperation_End = kFlexSpiOperation_Read, +} flexspi_operation_t; + +typedef struct _FlexSpiXfer +{ + flexspi_operation_t operation; //!< FlexSPI operation + uint32_t baseAddress; //!< FlexSPI operation base address + uint32_t seqId; //!< Sequence Id + uint32_t seqNum; //!< Sequence Number + bool isParallelModeEnable; //!< Is a parallel transfer + uint32_t *txBuffer; //!< Tx buffer + uint32_t txSize; //!< Tx size in bytes + uint32_t *rxBuffer; //!< Rx buffer + uint32_t rxSize; //!< Rx size in bytes +} flexspi_xfer_t; + +typedef struct +{ + uint32_t version; + status_t (*init)(uint32_t instance, flexspi_nor_config_t *config); + status_t (*program)(uint32_t instance, flexspi_nor_config_t *config, uint32_t +dst_addr, const uint32_t *src); + status_t (*erase_all)(uint32_t instance, flexspi_nor_config_t *config); + status_t (*erase)(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, +uint32_t lengthInBytes); + status_t (*read)( + uint32_t instance, flexspi_nor_config_t *config, uint32_t *dst, uint32_t addr, +uint32_t lengthInBytes); + void (*clear_cache)(uint32_t instance); + status_t (*xfer)(uint32_t instance, flexspi_xfer_t *xfer); + status_t (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t +*lutBase, uint32_t seqNumber); + status_t (*get_config)(uint32_t instance, flexspi_nor_config_t *config, +serial_nor_config_option_t *option); +} flexspi_nor_driver_interface_t; + +typedef struct +{ + const uint32_t version; //!< Bootloader version number + const char *copyright; //!< Bootloader Copyright + void (*runBootloader)(void *arg); //!< Function to start the bootloader executing + const uint32_t *reserved0; //!< Reserved + const flexspi_nor_driver_interface_t *flexSpiNorDriver; //!< FlexSPI NOR Flash API + const uint32_t *reserved1[2]; //!< Reserved + /*const rtwdog_driver_interface_t *rtwdogDriver; + const wdog_driver_interface_t *wdogDriver; + const uint32_t *reserved2;*/ +} bootloader_api_entry_t; + +#define bootloader (*(bootloader_api_entry_t**)(0x0020001c)) +#define flexspi_nor (*(flexspi_nor_driver_interface_t**) ((char*) bootloader + 4*sizeof(uint32_t))) + +static flexspi_nor_config_t s_config; +static serial_nor_config_option_t s_option; +static uint32_t s_instance; + +void *mg_flash_start(void) { + return (void *) 0x0; +} +size_t mg_flash_size(void) { + return 256 * 1024; // 256k +} +size_t mg_flash_sector_size(void) { + return 4 * 1024; // 4k +} +size_t mg_flash_write_align(void) { + return 256; +} +int mg_flash_bank(void) { + return 0; +} + +static bool flash_page_start(volatile uint32_t *dst) { + char *base = (char *) mg_flash_start(), *end = base + mg_flash_size(); + volatile char *p = (char *) dst; + return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0; +} + +uint32_t mg_flash_init() { + uint32_t status = 0; + s_option.option0.U = 0xc0000008; + status = flexspi_nor->get_config(s_instance, &s_config, &s_option); + if (status != 0) { + MG_DEBUG(("Failed to get flexspi configuration")); + return status; + } + + status = flexspi_nor->init(s_instance, &s_config); + return status; +} + +bool mg_flash_erase(void *addr) { + bool ok = false; + if (flash_page_start(addr) == false) { + MG_ERROR(("%p is not on a sector boundary", addr)); + } else { + uint32_t status = flexspi_nor->erase(s_instance, &s_config, (uint32_t) addr, mg_flash_sector_size()); + if (status == 0) ok = true; + } + if (ok) { + MG_DEBUG(("Successfully erased sector starting at %p", addr)); + } else { + MG_DEBUG(("Failed to erase sector starting at %p", addr)); + } + return ok; +} + +bool mg_flash_swap_bank() { + return false; +} + +bool mg_flash_write(void *addr, const void *buf, size_t len) { + if ((len % mg_flash_write_align()) != 0) { + MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align())); + return false; + } + uint32_t *dst = (uint32_t *) addr; + uint32_t *src = (uint32_t *) buf; + uint32_t *end = (uint32_t *) ((char *) buf + len); + bool ok = true; + while (ok && src < end) { + if (flash_page_start(dst) && mg_flash_erase(dst) == false) break; + uint32_t status = flexspi_nor->program(s_instance, &s_config, (uint32_t) dst, src); + src = (uint32_t *) ((char *) src + mg_flash_write_align()); + dst = (uint32_t *) ((char *) dst + mg_flash_write_align()); + if (status != 0) ok = false; + } + MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, + ok ? "ok" : "fail")); + return ok; +} + +void mg_device_reset(void) { + *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; +} +#endif + #ifdef MG_ENABLE_LINES #line 1 "src/device_stm32h5.c" #endif @@ -5700,6 +5895,107 @@ MG_IRAM void mg_ota_boot(void) { mg_device_reset(); } } + +#elif MG_OTA == MG_OTA_EXTERNAL_FLASH + +static char *s_addr; // Current address to write to +static size_t s_size; // Firmware size to flash. In-progress indicator +static uint32_t s_crc32; // Firmware checksum + +struct mg_otadata { + uint32_t crc32, size, timestamp, status; +}; + +bool mg_ota_begin(size_t new_firmware_size) { + bool ok = false; + if (s_size) { + MG_ERROR(("OTA already in progress. Call mg_ota_end()")); + } else { + if (new_firmware_size > mg_flash_size()) { + MG_ERROR(("Firmware exceeds flash size")); + return false; + } + s_size = new_firmware_size; + uint32_t flash_init_status = mg_flash_init(); + if (flash_init_status) { + MG_ERROR(("Error initialising the flash structure")); + return false; + } else { + ok = true; + } + } + return ok; +} + +bool mg_ota_write(const void *buf, size_t len) { + bool ok = false; + if (s_size == 0) { + MG_ERROR(("OTA is not started, call mg_ota_begin()")); + } else { + size_t align = mg_flash_write_align(); + size_t len_aligned_down = MG_ROUND_DOWN(len, align); + if (len_aligned_down) ok = mg_flash_write(s_addr, buf, len_aligned_down); + if (len_aligned_down < len) { + size_t left = len - len_aligned_down; + char tmp[align]; + memset(tmp, 0xff, sizeof(tmp)); + memcpy(tmp, (char *) buf + len_aligned_down, left); + ok = mg_flash_write(s_addr + len_aligned_down, tmp, sizeof(tmp)); + } + 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; +} + +bool mg_ota_end(void) { + bool ok = true; + size_t size = (size_t) s_addr; + uint32_t crc32 = mg_crc32(0, (char *) 0x60000000, size); + MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size, + size, s_crc32 == crc32 ? "ok" : "fail")); + s_addr = 0; + s_size = 0; + MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail")); + return ok; +} + +static struct mg_otadata mg_otadata(int fw) { + (void) fw; + struct mg_otadata od = {}; + return od; +} + +int mg_ota_status(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.status; +} + +uint32_t mg_ota_crc32(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.crc32; +} + +uint32_t mg_ota_timestamp(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.timestamp; +} + +size_t mg_ota_size(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.size; +} + +MG_IRAM bool mg_ota_commit(void) { + return false; +} + +bool mg_ota_rollback(void) { + MG_DEBUG(("Rolling firmware not supported yet")); + return false; +} + #endif #ifdef MG_ENABLE_LINES @@ -9309,7 +9605,7 @@ void ETH_IRQHandler(void) { } } #ifdef __riscv - ETH->DMASR = ~0; // TODO: do more fine-grained flag cleanup + ETH->DMASR = ~0; // TODO: do more fine-grained flag cleanup #else ETH->DMASR = MG_BIT(7); // Clear possible RBUS while processing #endif diff --git a/mongoose.h b/mongoose.h index c4464188606..4fe84e416c0 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1692,6 +1692,7 @@ void mg_rpc_list(struct mg_rpc_req *r); #define MG_OTA_NONE 0 // No OTA support #define MG_OTA_FLASH 1 // OTA via an internal flash +#define MG_OTA_EXTERNAL_FLASH 2 // OTA via an external flash #define MG_OTA_CUSTOM 100 // Custom implementation #ifndef MG_OTA @@ -1735,6 +1736,7 @@ MG_IRAM void mg_ota_boot(void); // Bootloader function #define MG_DEVICE_NONE 0 // Dummy system #define MG_DEVICE_STM32H5 1 // STM32 H5 #define MG_DEVICE_STM32H7 2 // STM32 H7 +#define MG_DEVICE_RT1060_QUAD_NOR 3 // IMXRT1060 QUAD SPI NOR FLASH #define MG_DEVICE_CUSTOM 100 // Custom implementation #ifndef MG_DEVICE diff --git a/src/device.h b/src/device.h index 99b1064bd81..8c3ccc6337f 100644 --- a/src/device.h +++ b/src/device.h @@ -8,6 +8,7 @@ #define MG_DEVICE_NONE 0 // Dummy system #define MG_DEVICE_STM32H5 1 // STM32 H5 #define MG_DEVICE_STM32H7 2 // STM32 H7 +#define MG_DEVICE_RT1060_QUAD_NOR 3 // IMXRT1060 QUAD SPI NOR FLASH #define MG_DEVICE_CUSTOM 100 // Custom implementation #ifndef MG_DEVICE diff --git a/src/device_rt1060_quad_nor.c b/src/device_rt1060_quad_nor.c new file mode 100644 index 00000000000..8218b7c9eac --- /dev/null +++ b/src/device_rt1060_quad_nor.c @@ -0,0 +1,191 @@ +#include "device.h" +#include "log.h" + +#if MG_DEVICE == MG_DEVICE_RT1060_QUAD_NOR + +typedef struct { + char memCfg[448]; + uint32_t pageSize; + uint32_t sectorSize; + uint32_t ipCmdSerialClkFreq; + char reserved[55]; +} flexspi_nor_config_t; + +typedef struct _serial_nor_config_option +{ + union + { + struct + { + uint32_t max_freq : 4; //!< Maximum supported Frequency + uint32_t misc_mode : 4; //!< miscellaneous mode + uint32_t quad_mode_setting : 4; //!< Quad mode setting + uint32_t cmd_pads : 4; //!< Command pads + uint32_t query_pads : 4; //!< SFDP read pads + uint32_t device_type : 4; //!< Device type + uint32_t option_size : 4; //!< Option size, in terms of uint32_t, size = (option_size + 1) * 4 + uint32_t tag : 4; //!< Tag, must be 0x0E + } B; + uint32_t U; + } option0; + union + { + struct + { + uint32_t dummy_cycles : 8; //!< Dummy cycles before read + uint32_t reserved0 : 8; //!< Reserved for future use + uint32_t pinmux_group : 4; //!< The pinmux group selection + uint32_t reserved1 : 8; //!< Reserved for future use + uint32_t flash_connection : 4; //!< Flash connection option: 0 - Single Flash connected to port A + } B; + uint32_t U; + } option1; +} serial_nor_config_option_t; + +typedef int status_t; + +typedef enum _FlexSPIOperationType +{ + kFlexSpiOperation_Command, //!< FlexSPI operation: Only command, both TX and + //! RX buffer are ignored. + kFlexSpiOperation_Config, //!< FlexSPI operation: Configure device mode, the + //! TX FIFO size is fixed in LUT. + kFlexSpiOperation_Write, //!< FlexSPI operation: Write, only TX buffer is + //! effective + kFlexSpiOperation_Read, //!< FlexSPI operation: Read, only Rx Buffer is + kFlexSpiOperation_End = kFlexSpiOperation_Read, +} flexspi_operation_t; + +typedef struct _FlexSpiXfer +{ + flexspi_operation_t operation; //!< FlexSPI operation + uint32_t baseAddress; //!< FlexSPI operation base address + uint32_t seqId; //!< Sequence Id + uint32_t seqNum; //!< Sequence Number + bool isParallelModeEnable; //!< Is a parallel transfer + uint32_t *txBuffer; //!< Tx buffer + uint32_t txSize; //!< Tx size in bytes + uint32_t *rxBuffer; //!< Rx buffer + uint32_t rxSize; //!< Rx size in bytes +} flexspi_xfer_t; + +typedef struct +{ + uint32_t version; + status_t (*init)(uint32_t instance, flexspi_nor_config_t *config); + status_t (*program)(uint32_t instance, flexspi_nor_config_t *config, uint32_t +dst_addr, const uint32_t *src); + status_t (*erase_all)(uint32_t instance, flexspi_nor_config_t *config); + status_t (*erase)(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, +uint32_t lengthInBytes); + status_t (*read)( + uint32_t instance, flexspi_nor_config_t *config, uint32_t *dst, uint32_t addr, +uint32_t lengthInBytes); + void (*clear_cache)(uint32_t instance); + status_t (*xfer)(uint32_t instance, flexspi_xfer_t *xfer); + status_t (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t +*lutBase, uint32_t seqNumber); + status_t (*get_config)(uint32_t instance, flexspi_nor_config_t *config, +serial_nor_config_option_t *option); +} flexspi_nor_driver_interface_t; + +typedef struct +{ + const uint32_t version; //!< Bootloader version number + const char *copyright; //!< Bootloader Copyright + void (*runBootloader)(void *arg); //!< Function to start the bootloader executing + const uint32_t *reserved0; //!< Reserved + const flexspi_nor_driver_interface_t *flexSpiNorDriver; //!< FlexSPI NOR Flash API + const uint32_t *reserved1[2]; //!< Reserved + /*const rtwdog_driver_interface_t *rtwdogDriver; + const wdog_driver_interface_t *wdogDriver; + const uint32_t *reserved2;*/ +} bootloader_api_entry_t; + +#define bootloader (*(bootloader_api_entry_t**)(0x0020001c)) +#define flexspi_nor (*(flexspi_nor_driver_interface_t**) ((char*) bootloader + 4*sizeof(uint32_t))) + +static flexspi_nor_config_t s_config; +static serial_nor_config_option_t s_option; +static uint32_t s_instance; + +void *mg_flash_start(void) { + return (void *) 0x0; +} +size_t mg_flash_size(void) { + return 256 * 1024; // 256k +} +size_t mg_flash_sector_size(void) { + return 4 * 1024; // 4k +} +size_t mg_flash_write_align(void) { + return 256; +} +int mg_flash_bank(void) { + return 0; +} + +static bool flash_page_start(volatile uint32_t *dst) { + char *base = (char *) mg_flash_start(), *end = base + mg_flash_size(); + volatile char *p = (char *) dst; + return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0; +} + +uint32_t mg_flash_init() { + uint32_t status = 0; + s_option.option0.U = 0xc0000008; + status = flexspi_nor->get_config(s_instance, &s_config, &s_option); + if (status != 0) { + MG_DEBUG(("Failed to get flexspi configuration")); + return status; + } + + status = flexspi_nor->init(s_instance, &s_config); + return status; +} + +bool mg_flash_erase(void *addr) { + bool ok = false; + if (flash_page_start(addr) == false) { + MG_ERROR(("%p is not on a sector boundary", addr)); + } else { + uint32_t status = flexspi_nor->erase(s_instance, &s_config, (uint32_t) addr, mg_flash_sector_size()); + if (status == 0) ok = true; + } + if (ok) { + MG_DEBUG(("Successfully erased sector starting at %p", addr)); + } else { + MG_DEBUG(("Failed to erase sector starting at %p", addr)); + } + return ok; +} + +bool mg_flash_swap_bank() { + return false; +} + +bool mg_flash_write(void *addr, const void *buf, size_t len) { + if ((len % mg_flash_write_align()) != 0) { + MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align())); + return false; + } + uint32_t *dst = (uint32_t *) addr; + uint32_t *src = (uint32_t *) buf; + uint32_t *end = (uint32_t *) ((char *) buf + len); + bool ok = true; + while (ok && src < end) { + if (flash_page_start(dst) && mg_flash_erase(dst) == false) break; + uint32_t status = flexspi_nor->program(s_instance, &s_config, (uint32_t) dst, src); + src = (uint32_t *) ((char *) src + mg_flash_write_align()); + dst = (uint32_t *) ((char *) dst + mg_flash_write_align()); + if (status != 0) ok = false; + } + MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, + ok ? "ok" : "fail")); + return ok; +} + +void mg_device_reset(void) { + *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; +} +#endif diff --git a/src/ota.h b/src/ota.h index 7eae2a496a0..5cadd9e6b25 100644 --- a/src/ota.h +++ b/src/ota.h @@ -7,6 +7,7 @@ #define MG_OTA_NONE 0 // No OTA support #define MG_OTA_FLASH 1 // OTA via an internal flash +#define MG_OTA_EXTERNAL_FLASH 2 // OTA via an external flash #define MG_OTA_CUSTOM 100 // Custom implementation #ifndef MG_OTA diff --git a/src/ota_flash.c b/src/ota_flash.c index d5fe87b05b5..4084e41f707 100644 --- a/src/ota_flash.c +++ b/src/ota_flash.c @@ -192,4 +192,105 @@ MG_IRAM void mg_ota_boot(void) { mg_device_reset(); } } + +#elif MG_OTA == MG_OTA_EXTERNAL_FLASH + +static char *s_addr; // Current address to write to +static size_t s_size; // Firmware size to flash. In-progress indicator +static uint32_t s_crc32; // Firmware checksum + +struct mg_otadata { + uint32_t crc32, size, timestamp, status; +}; + +bool mg_ota_begin(size_t new_firmware_size) { + bool ok = false; + if (s_size) { + MG_ERROR(("OTA already in progress. Call mg_ota_end()")); + } else { + if (new_firmware_size > mg_flash_size()) { + MG_ERROR(("Firmware exceeds flash size")); + return false; + } + s_size = new_firmware_size; + uint32_t flash_init_status = mg_flash_init(); + if (flash_init_status) { + MG_ERROR(("Error initialising the flash structure")); + return false; + } else { + ok = true; + } + } + return ok; +} + +bool mg_ota_write(const void *buf, size_t len) { + bool ok = false; + if (s_size == 0) { + MG_ERROR(("OTA is not started, call mg_ota_begin()")); + } else { + size_t align = mg_flash_write_align(); + size_t len_aligned_down = MG_ROUND_DOWN(len, align); + if (len_aligned_down) ok = mg_flash_write(s_addr, buf, len_aligned_down); + if (len_aligned_down < len) { + size_t left = len - len_aligned_down; + char tmp[align]; + memset(tmp, 0xff, sizeof(tmp)); + memcpy(tmp, (char *) buf + len_aligned_down, left); + ok = mg_flash_write(s_addr + len_aligned_down, tmp, sizeof(tmp)); + } + 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; +} + +bool mg_ota_end(void) { + bool ok = true; + size_t size = (size_t) s_addr; + uint32_t crc32 = mg_crc32(0, (char *) 0x60000000, size); + MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size, + size, s_crc32 == crc32 ? "ok" : "fail")); + s_addr = 0; + s_size = 0; + MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail")); + return ok; +} + +static struct mg_otadata mg_otadata(int fw) { + (void) fw; + struct mg_otadata od = {}; + return od; +} + +int mg_ota_status(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.status; +} + +uint32_t mg_ota_crc32(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.crc32; +} + +uint32_t mg_ota_timestamp(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.timestamp; +} + +size_t mg_ota_size(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.size; +} + +MG_IRAM bool mg_ota_commit(void) { + return false; +} + +bool mg_ota_rollback(void) { + MG_DEBUG(("Rolling firmware not supported yet")); + return false; +} + #endif