From aa8151c3d6a6ec31137b0ec5d09515bb5ea770dd Mon Sep 17 00:00:00 2001 From: Michael Sauer Date: Sat, 12 Sep 2026 01:36:57 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20OTA=20=C3=BCber=20HTTP=20mit=20Trailer-?= =?UTF-8?q?Pr=C3=BCfung?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- sdk/PATCHES.md | 4 +- src/app_main.c | 10 ++++ src/sys/fwup_hook_default.c | 10 ---- src/sys/nl_app_api.c | 9 +-- src/sys/nl_fwup_hook.h | 3 +- src/sys/nl_ota.c | 113 ++++++++++++++++++++++++++++++++++++ src/sys/nl_ota.h | 14 +++++ 8 files changed, 146 insertions(+), 19 deletions(-) delete mode 100644 src/sys/fwup_hook_default.c create mode 100644 src/sys/nl_ota.c create mode 100644 src/sys/nl_ota.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2285a04..0fae211 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,7 @@ set(NL_FW_SOURCES src/drivers/nl_sm2135eh.c src/sys/nl_console_cmd.c src/sys/nl_console.c - src/sys/fwup_hook_default.c + src/sys/nl_ota.c src/sys/sdk_stubs.c src/net/nl_names.c src/net/nl_http.c diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md index 46ff0ea..1077e86 100644 --- a/sdk/PATCHES.md +++ b/sdk/PATCHES.md @@ -6,7 +6,9 @@ Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ 1. `Platform/Common/fwup/wm_fwup.c`: wolfSSL-SHA256 durch `nl_sha256_*` ersetzt, die QConnex-RSA-Prüfung `Check_OTA_Image()` durch den Hook `nl_fwup_verify_tail()` (src/sys/nl_fwup_hook.h). Das Image-Format - (Header + Payload + 512-Byte-Endblock) bleibt unverändert. + (Header + Payload + 512-Byte-Endblock) bleibt unverändert. Die starke Definition des Hooks liegt seit + Plan 3 in `src/sys/nl_ota.c` (Trailer-Prüfung über `nl_trailer_verify()`); die bisherige `weak`-Variante + `src/sys/fwup_hook_default.c` ist entfallen. 2. `Src/OS/RTOS/include/FreeRTOSConfig.h`: `configGENERATE_RUN_TIME_STATS 0`, QConnex-Debug-Hooks entfernt. 3. `undefined reference` beim Linken auf `EarlyInit` und `pre_gpio_config` (von `sdk/Platform/Sys/wm_main.c` als `extern`-Hooks erwartet, aber nur von der App bereitgestellt): diff --git a/src/app_main.c b/src/app_main.c index 82bee8b..d2dc86b 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -20,6 +20,7 @@ #include "sys/nl_app.h" #include "sys/nl_app_api.h" #include "sys/nl_console.h" +#include "sys/nl_ota.h" #include "nl_version.h" @@ -163,6 +164,14 @@ static void app_task(void *arg) nl_state_log_init(base + NL_FL_STATE_0, base + NL_FL_STATE_1); have_last = nl_state_log_load(&last); store_unlock(); + if (s_settings.ota_pending) { + /* We came back from an update attempt: restore the light the user had on. */ + s_settings.ota_pending = 0; + printf("boot after ota attempt\r\n"); + store_lock(); + (void)nl_settings_save(&s_settings); + store_unlock(); + } printf("boot #%u, settings: pob=%u fade=%u/%u, last state: %s\r\n", boots, s_settings.power_on_behaviour, s_settings.fade_in_ms, s_settings.fade_out_ms, have_last ? "yes" : "no"); @@ -184,6 +193,7 @@ static void app_task(void *arg) nl_app_factory_reset(); /* does not return */ tls_watchdog_init(20u * 1000u * 1000u); /* 20 s */ + nl_ota_init(); nl_console_start(); { diff --git a/src/sys/fwup_hook_default.c b/src/sys/fwup_hook_default.c deleted file mode 100644 index 6b4d7c4..0000000 --- a/src/sys/fwup_hook_default.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "sys/nl_fwup_hook.h" - -/* Until OTA support (plan 3) provides a strong definition, every update is rejected. */ -__attribute__((weak)) bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t tail_len) -{ - (void)sha256; - (void)tail; - (void)tail_len; - return false; -} diff --git a/src/sys/nl_app_api.c b/src/sys/nl_app_api.c index c761bcc..f6d5fc4 100644 --- a/src/sys/nl_app_api.c +++ b/src/sys/nl_app_api.c @@ -11,6 +11,7 @@ #include "net/nl_wifi.h" #include "sys/nl_app.h" #include "sys/nl_app_api.h" +#include "sys/nl_ota.h" #include "nl_version.h" @@ -88,7 +89,7 @@ static void get_info(nl_sys_info_t *out) out->wifi_error = w.error; out->uptime_s = nl_app_now_ms() / 1000u; out->free_heap = tls_mem_get_avail_heapsize(); - out->last_ota_error = NULL; + out->last_ota_error = nl_ota_last_error(); } static void factory_reset(void) { nl_app_request_factory_reset(); } @@ -100,11 +101,7 @@ static void ping(void) nl_app_unlock(); } -static int fw_update(const char *url) -{ - (void)url; - return -1; /* OTA arrives in plan 3 */ -} +static int fw_update(const char *url) { return nl_ota_start(url); } const nl_api_ctx_t *nl_app_api_ctx(void) { diff --git a/src/sys/nl_fwup_hook.h b/src/sys/nl_fwup_hook.h index ff85122..6d02275 100644 --- a/src/sys/nl_fwup_hook.h +++ b/src/sys/nl_fwup_hook.h @@ -6,7 +6,8 @@ /* Called by the SDK updater (sdk/Platform/Common/fwup/wm_fwup.c) after the image CRC * matched. sha256 covers the T_BOOTER header plus payload, tail is the 512 byte block - * appended to the image. Return true to accept the image. */ + * appended to the image. Return true to accept the image. + * Strong definition: src/sys/nl_ota.c (checks the trailer via nl_trailer_verify()). */ bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t tail_len); #endif diff --git a/src/sys/nl_ota.c b/src/sys/nl_ota.c new file mode 100644 index 0000000..906b550 --- /dev/null +++ b/src/sys/nl_ota.c @@ -0,0 +1,113 @@ +#include +#include + +#include "wm_include.h" +#include "wm_http_fwup.h" + +#include "config/nl_settings.h" +#include "sys/nl_app.h" +#include "sys/nl_fwup_hook.h" +#include "sys/nl_ota.h" +#include "util/nl_fwup_trailer.h" + +/* t_http_fwup() (sdk/Src/App/OTA/wm_http_fwup.c) has no declaration in any SDK header. */ +extern int t_http_fwup(char *url); + +#define NL_OTA_URL_MAX 256 +#define NL_OTA_STACK 2048 /* words */ +#define NL_OTA_PRIO 34 + +static char s_url[NL_OTA_URL_MAX]; +static volatile bool s_busy; +static const char * volatile s_error; +static u32 s_stack[NL_OTA_STACK]; + +void nl_ota_init(void) +{ + s_busy = false; + s_error = NULL; +} + +bool nl_ota_busy(void) { return s_busy; } +const char *nl_ota_last_error(void) { return s_error; } +void nl_ota_set_error(const char *e) { s_error = e; } + +/* Called by the SDK firmware-update path once the image has been written and hashed. + sha256 covers the whole payload (T_BOOTER header + compressed image), tail is the + 512 byte end block that used to carry the QConnex RSA signature. */ +bool nl_fwup_verify_tail(const uint8_t sha256[32], const uint8_t *tail, uint32_t tail_len) +{ + nl_trailer_err_t e = nl_trailer_verify(tail, tail_len, NINLIGHT_DEVICE_ID, sha256); + + if (e != NL_TRAILER_OK) { + s_error = nl_trailer_err_name(e); + printf("ota: trailer rejected (%s)\r\n", s_error); + return false; + } + { + char v[NL_TRAILER_VERSION_LEN]; + nl_trailer_version(tail, v); + printf("ota: trailer ok, incoming version %s\r\n", v); + } + return true; +} + +static void ota_task(void *arg) +{ + int rc; + + (void)arg; + printf("ota: downloading %s\r\n", s_url); + rc = t_http_fwup(s_url); + /* Only reached on failure - the SDK resets the chip itself after a good image. */ + if (s_error == NULL) + s_error = "download"; + printf("ota: failed (rc=%d, %s)\r\n", rc, s_error); + { + nl_settings_t *s; + nl_app_lock(); + s = nl_app_settings(); + s->ota_pending = 0; + nl_app_unlock(); + (void)nl_app_save_settings(); + } + s_busy = false; + tls_os_task_del(NL_OTA_PRIO, NULL); +} + +int nl_ota_start(const char *url) +{ + size_t n; + + if (url == NULL || strncmp(url, "http://", 7) != 0) + return -1; + n = strlen(url); + if (n == 0 || n >= sizeof s_url) + return -1; + if (s_busy) { + s_error = "busy"; + return -1; + } + s_busy = true; + s_error = NULL; + memcpy(s_url, url, n + 1); + + /* Persist the open light state and mark the pending update before the flash gets busy. */ + nl_app_flush(); + { + nl_settings_t *s; + nl_app_lock(); + s = nl_app_settings(); + s->ota_pending = 1; + nl_app_unlock(); + (void)nl_app_save_settings(); + } + + if (tls_os_task_create(NULL, "nl_ota", ota_task, NULL, (u8 *)s_stack, sizeof(s_stack), + NL_OTA_PRIO, 0) != TLS_OS_SUCCESS) { + s_busy = false; + s_error = "busy"; + return -1; + } + return 0; +} diff --git a/src/sys/nl_ota.h b/src/sys/nl_ota.h new file mode 100644 index 0000000..e1e6c7f --- /dev/null +++ b/src/sys/nl_ota.h @@ -0,0 +1,14 @@ +#ifndef NL_OTA_H +#define NL_OTA_H + +#include + +void nl_ota_init(void); +/* Starts a download task for an http:// URL. 0 = started, -1 = busy or invalid. */ +int nl_ota_start(const char *url); +bool nl_ota_busy(void); +/* NULL, or one of: download, crc, trailer, device_mismatch, hash, busy */ +const char *nl_ota_last_error(void); +void nl_ota_set_error(const char *e); + +#endif