fix: Recovery-Rettungsweg härten (Watchdog-Cap, Konsole, Softap-Bit, Scan-Retry)
This commit is contained in:
+9
-2
@@ -44,9 +44,16 @@ endif()
|
||||
if(NOT DEFINED NINLIGHT_RECOVERY_PASSWORD)
|
||||
set(NINLIGHT_RECOVERY_PASSWORD "ninlight-recovery")
|
||||
endif()
|
||||
# Unlike NINLIGHT_WIFI_SSID, the recovery SSID is compared byte-for-byte against scan
|
||||
# results (nl_recovery.c) and must fit struct tls_bss_info_t::ssid[32]; empty would match
|
||||
# any hidden/empty-SSID AP in the scan, so it must not be empty either.
|
||||
string(LENGTH "${NINLIGHT_RECOVERY_SSID}" _nl_rec_ssid_len)
|
||||
if(_nl_rec_ssid_len EQUAL 0 OR _nl_rec_ssid_len GREATER 32)
|
||||
message(FATAL_ERROR "NINLIGHT_RECOVERY_SSID must be 1..32 characters")
|
||||
endif()
|
||||
string(LENGTH "${NINLIGHT_RECOVERY_PASSWORD}" _nl_rec_len)
|
||||
if(_nl_rec_len LESS 8)
|
||||
message(FATAL_ERROR "NINLIGHT_RECOVERY_PASSWORD needs at least 8 characters (WPA2)")
|
||||
if(_nl_rec_len LESS 8 OR _nl_rec_len GREATER 63)
|
||||
message(FATAL_ERROR "NINLIGHT_RECOVERY_PASSWORD must be 8..63 characters (WPA2)")
|
||||
endif()
|
||||
foreach(_v NINLIGHT_WIFI_SSID NINLIGHT_WIFI_PASSWORD NINLIGHT_AP_PASSWORD NINLIGHT_TOKEN
|
||||
NINLIGHT_RECOVERY_SSID NINLIGHT_RECOVERY_PASSWORD)
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
# set(NINLIGHT_WIFI_SSID "MyWifi")
|
||||
# set(NINLIGHT_WIFI_PASSWORD "secret")
|
||||
# set(NINLIGHT_AP_PASSWORD "ninlight")
|
||||
# Boot recovery station (plan 3): SSID 1..32 chars, password 8..63 chars (WPA2)
|
||||
# set(NINLIGHT_RECOVERY_SSID "ninlight-recovery")
|
||||
# set(NINLIGHT_RECOVERY_PASSWORD "ninlight-recovery")
|
||||
# Used from plan 3/4 on (path to the old RSA signing key):
|
||||
# set(NINLIGHT_MIGRATION_KEY "/path/to/fw_sign.pem")
|
||||
|
||||
+1
-1
@@ -197,8 +197,8 @@ static void app_task(void *arg)
|
||||
|
||||
tls_watchdog_init(20u * 1000u * 1000u); /* 20 s */
|
||||
nl_ota_init();
|
||||
nl_console_start(); /* keep the serial fallback alive during nl_recovery_try() below */
|
||||
nl_recovery_try(); /* may not return: a successful recovery resets the chip */
|
||||
nl_console_start();
|
||||
|
||||
{
|
||||
static nl_settings_t boot; /* copy: the network task may change s_settings later */
|
||||
|
||||
+57
-5
@@ -12,6 +12,15 @@
|
||||
#define NL_RECOVERY_SCAN_MS 3000u
|
||||
#define NL_RECOVERY_JOIN_MS 15000u
|
||||
#define NL_RECOVERY_BUF 2048u
|
||||
/* Generous cap on how long the download itself may run. wm_http_fwup.c's own resume loop
|
||||
can already take up to ~20 minutes on a slow/aborted transfer (200 rounds x up to 5 s
|
||||
receive timeout / 2 s retry delay), and a real hang - e.g. its unbounded
|
||||
"while (!p) { tls_os_time_delay(1); pbuf_alloc(...) }" retry on allocation failure
|
||||
(wm_http_fwup.c:178) - never returns from t_http_fwup() at all. Past this cap we stop
|
||||
feeding the watchdog (see below) instead of waiting indefinitely: a watchdog reset is
|
||||
strictly better than a silent hang with no Wi-Fi and no console, and a fresh boot simply
|
||||
retries recovery from scratch. */
|
||||
#define NL_RECOVERY_DOWNLOAD_MAX_MS (10u * 60u * 1000u)
|
||||
|
||||
static volatile bool s_scan_done;
|
||||
|
||||
@@ -32,24 +41,50 @@ static bool recovery_ap_present(void)
|
||||
uint32_t max_entries, n;
|
||||
bool found = false;
|
||||
u32 i;
|
||||
int scan_rc;
|
||||
|
||||
if (buf == NULL)
|
||||
return false;
|
||||
s_scan_done = false;
|
||||
tls_wifi_scan_result_cb_register(scan_done_cb);
|
||||
if (tls_wifi_scan() != WM_SUCCESS) {
|
||||
|
||||
/* wm_wifi.h: "If not SUCCESS, user needs to call this function again to trigger scan" -
|
||||
retry a transient failure (e.g. WM_WIFI_SCANNING_BUSY) instead of silently skipping
|
||||
the whole recovery attempt, but stay inside the shared 3 s budget. */
|
||||
for (;;) {
|
||||
scan_rc = tls_wifi_scan();
|
||||
if (scan_rc == WM_SUCCESS)
|
||||
break;
|
||||
if (waited >= NL_RECOVERY_SCAN_MS) {
|
||||
printf("recovery: tls_wifi_scan() failed (rc=%d), giving up\r\n", scan_rc);
|
||||
tls_wifi_scan_result_cb_register(NULL);
|
||||
tls_mem_free(buf);
|
||||
return false;
|
||||
}
|
||||
while (!s_scan_done && waited < NL_RECOVERY_SCAN_MS) {
|
||||
tls_watchdog_clr(); /* boot phase: only this path runs, the light_task heartbeat
|
||||
gate in app_task() does not watch us yet */
|
||||
tls_os_time_delay(HZ / 10);
|
||||
waited += 100;
|
||||
}
|
||||
if (s_scan_done && tls_wifi_get_scan_rslt((u8 *)buf, NL_RECOVERY_BUF) == WM_SUCCESS) {
|
||||
|
||||
while (!s_scan_done && waited < NL_RECOVERY_SCAN_MS) {
|
||||
tls_watchdog_clr();
|
||||
tls_os_time_delay(HZ / 10);
|
||||
waited += 100;
|
||||
}
|
||||
|
||||
if (!s_scan_done) {
|
||||
/* Without this line, a scan whose callback simply never fired in time is
|
||||
indistinguishable in the log from "no AP found" - the EVK verification needs
|
||||
exactly that distinction. */
|
||||
printf("recovery: scan result not ready after %u ms, giving up\r\n", (unsigned)waited);
|
||||
} else if (tls_wifi_get_scan_rslt((u8 *)buf, NL_RECOVERY_BUF) != WM_SUCCESS) {
|
||||
printf("recovery: scan done after %u ms, but tls_wifi_get_scan_rslt() failed\r\n",
|
||||
(unsigned)waited);
|
||||
} else {
|
||||
res = (struct tls_scan_bss_t *)buf;
|
||||
printf("recovery: scan done after %u ms, %u AP(s)\r\n", (unsigned)waited,
|
||||
(unsigned)res->count);
|
||||
/* struct tls_bss_info_t has no length field - res->bss is a fixed-size-element
|
||||
array (struct tls_scan_bss_t::bss[1] is a flexible-array-style head), so index
|
||||
it directly. Cap by what actually fits in buf in case res->count ever reports
|
||||
@@ -88,11 +123,28 @@ void nl_recovery_try(void)
|
||||
printf("recovery: cannot start download\r\n");
|
||||
return;
|
||||
}
|
||||
/* Give the download task the boot phase; on success the SDK resets the chip. */
|
||||
/* Give the download task the boot phase; on success the SDK resets the chip. Past
|
||||
NL_RECOVERY_DOWNLOAD_MAX_MS, stop feeding the watchdog instead of continuing to wait -
|
||||
see the comment on that constant. */
|
||||
{
|
||||
uint32_t waited = 0;
|
||||
bool feeding = true;
|
||||
|
||||
while (nl_ota_busy()) {
|
||||
if (feeding) {
|
||||
if (waited >= NL_RECOVERY_DOWNLOAD_MAX_MS) {
|
||||
printf("recovery: download still running after %u ms, letting the watchdog reset\r\n",
|
||||
(unsigned)NL_RECOVERY_DOWNLOAD_MAX_MS);
|
||||
feeding = false;
|
||||
} else {
|
||||
tls_watchdog_clr();
|
||||
tls_os_time_delay(HZ / 2);
|
||||
}
|
||||
}
|
||||
tls_os_time_delay(HZ / 2);
|
||||
waited += 500;
|
||||
}
|
||||
if (feeding)
|
||||
printf("recovery: update failed (%s), continuing normal boot\r\n",
|
||||
nl_ota_last_error() ? nl_ota_last_error() : "unknown");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,6 +345,13 @@ bool nl_wifi_join_blocking(const char *ssid, const char *pass, uint32_t timeout_
|
||||
{
|
||||
uint32_t waited = 0;
|
||||
|
||||
/* C2: if a stale SDK softap bit from a previous onboarding/fallback boot is still set
|
||||
* in TLS_PARAM_ID_WPROTOCOL, sys_net_status_changed() (sdk/Platform/Sys/tls_sys.c)
|
||||
* recreates the SDK's own default-params softap the moment this join reaches
|
||||
* NETIF_IP_NET_UP - exactly the "lamp stuck in onboarding" case recovery exists for.
|
||||
* Clear it once, up front, same as nl_wifi_start() does. */
|
||||
clear_softap_param();
|
||||
|
||||
s_recovery_join_done = false;
|
||||
s_recovery_join_ok = false;
|
||||
tls_netif_add_status_event(recovery_join_event);
|
||||
|
||||
Reference in New Issue
Block a user