583 lines
23 KiB
C
583 lines
23 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "wm_include.h"
|
|
#include "wm_efuse.h"
|
|
#include "wm_watchdog.h"
|
|
#include "lwip/ip_addr.h"
|
|
#include "lwip/netif.h" /* struct netif is only forward-declared by wm_netif2.0.3.h */
|
|
|
|
#include "light/nl_engine.h"
|
|
#include "net/nl_mdns.h"
|
|
#include "net/nl_names.h"
|
|
#include "net/nl_wifi.h"
|
|
#include "net/nl_wifi_sm.h"
|
|
#include "sys/nl_app.h"
|
|
|
|
#define NL_NET_PRIO 35
|
|
#define NL_NET_QUEUE 8
|
|
#define MSG_NETIF 0x100u /* | netif status */
|
|
#define MSG_ONBOARD 0x200u
|
|
/* 0x400, nicht 0x300: die netif-Meldungen werden weiter unten mit (m & MSG_NETIF) erkannt,
|
|
* und 0x300 traegt das 0x100-Bit mit - die Standalone-Meldung waere als netif-Ereignis
|
|
* protokolliert worden. */
|
|
#define MSG_STANDALONE 0x400u
|
|
|
|
static u32 s_stack[768];
|
|
static tls_os_queue_t *s_queue;
|
|
static tls_os_mutex_t *s_lock; /* guards s_sm and the credential buffers */
|
|
static nl_wifi_sm_t s_sm;
|
|
static bool s_started;
|
|
static uint8_t s_mac[6];
|
|
static char s_hostname[24];
|
|
static char s_ssid[NL_SSID_LEN], s_pass[NL_PASS_LEN];
|
|
static char s_test_ssid[NL_SSID_LEN], s_test_pass[NL_PASS_LEN];
|
|
static char s_ap_pass[NL_PASS_LEN];
|
|
static char s_ap_ssid[NL_SSID_LEN]; /* leer = Hostname */
|
|
static uint8_t s_ap_channel = 6;
|
|
/* Die Daten, mit denen der laufende AP tatsaechlich aufgebaut wurde. Nur so ist zu sehen,
|
|
* ob ein Wunsch aus der API den AP wirklich veraendert - die Zustandsmaschine liefert kein
|
|
* start_ap, wenn der AP schon steht, und die alten Daten blieben sonst still in Kraft. */
|
|
static char s_ap_live_ssid[NL_SSID_LEN], s_ap_live_pass[NL_PASS_LEN];
|
|
static uint8_t s_ap_live_channel;
|
|
static bool s_ap_live;
|
|
/* Drei Versuche, dann der Rueckfall auf Daten, die schon einmal getragen haben: einer koennte
|
|
* ein Zufall sein (das Funkteil raeumt gerade den alten AP ab), aber drei hintereinander
|
|
* heissen, dass das Funkteil genau diese Daten nicht annimmt. Und 3 x 5 s hoechstens
|
|
* 15 s ohne AP ist kuerzer als jede Fehlersuche vor Ort. */
|
|
#define NL_AP_CREATE_TRIES 3
|
|
static uint8_t s_ap_create_fails;
|
|
/* Vom HTTP-Task gefuellt, vom Netz-Task uebernommen; beides unter s_lock. */
|
|
static char s_req_ap_ssid[NL_SSID_LEN], s_req_ap_pass[NL_PASS_LEN];
|
|
static uint8_t s_req_ap_channel;
|
|
static bool s_req_enabled, s_req_pulse, s_req_pending;
|
|
static bool s_ap_create_pending; /* M3: tls_wifi_softap_create() failed, retry it */
|
|
static uint32_t s_ap_create_retry_at;
|
|
|
|
volatile uint32_t nl_net_beat;
|
|
|
|
static void lock(void) { tls_os_mutex_acquire(s_lock, 0); }
|
|
static void unlock(void) { tls_os_mutex_release(s_lock); }
|
|
|
|
static void copy(char *dst, const char *src, size_t size)
|
|
{
|
|
strncpy(dst, src, size - 1);
|
|
dst[size - 1] = '\0';
|
|
}
|
|
|
|
static bool due_now(uint32_t now, uint32_t at) { return (int32_t)(now - at) >= 0; }
|
|
|
|
/* SDK system task context: only hand the event over. */
|
|
static void netif_event(u8 status)
|
|
{
|
|
tls_os_queue_send(s_queue, (void *)(MSG_NETIF | status), 0);
|
|
}
|
|
|
|
/* C2: the SDK sets the SOFTAP bit in TLS_PARAM_ID_WPROTOCOL when tls_wifi_softap_create()
|
|
* runs and persists it to flash; tls_wifi_softap_destroy() only sends a supplicant event
|
|
* and never clears it. As long as the bit stays set, sys_net_status_changed() in
|
|
* sdk/Platform/Sys/tls_sys.c (NETIF_IP_NET_UP case) recreates the AP with the SDK's own
|
|
* (public default) softap params on every station IP-up - independent of our auto-connect
|
|
* flag. Clear the bit (and persist that) whenever we know the AP should be down. */
|
|
static void clear_softap_param(void)
|
|
{
|
|
u8 proto = 0;
|
|
|
|
tls_param_get(TLS_PARAM_ID_WPROTOCOL, &proto, FALSE);
|
|
if (proto & IEEE80211_MODE_AP) {
|
|
proto &= (u8)~IEEE80211_MODE_AP;
|
|
tls_param_set(TLS_PARAM_ID_WPROTOCOL, &proto, TRUE);
|
|
printf("wifi: cleared stale SDK softap param (wprotocol now %u)\r\n", proto);
|
|
}
|
|
}
|
|
|
|
/* I3: tls_sys_auto_mode_run() (sdk/Platform/Sys/tls_sys.c) kicks off an SDK-driven join right
|
|
* after UserMain() whenever the persisted auto-connect flag is on - and tls_sys.c:264-267 turns a
|
|
* temporary WIFI_AUTO_CNT_TMP_OFF back into WIFI_AUTO_CNT_ON and persists that, so the factory
|
|
* default "off" is not something we may rely on. Any such join is indistinguishable from our own
|
|
* in the netif status events, so switch it off (and drop a connection it already made) before the
|
|
* recovery scan, not only in nl_wifi_start(). */
|
|
void nl_wifi_disable_sdk_auto_connect(void)
|
|
{
|
|
u8 auto_mode = WIFI_AUTO_CNT_OFF;
|
|
|
|
tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_mode);
|
|
if (auto_mode != WIFI_AUTO_CNT_OFF) {
|
|
printf("wifi: SDK auto-connect was %u, switching it off\r\n", auto_mode);
|
|
auto_mode = WIFI_AUTO_CNT_OFF;
|
|
tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_mode);
|
|
}
|
|
tls_wifi_disconnect();
|
|
}
|
|
|
|
/* Setzt Name, Passwort und Kanal des naechsten AP auf die Vorgaben zurueck: "ninlight-<mac4>"
|
|
* mit dem einkompilierten Passwort auf Kanal 6. Beruehrt einen LAUFENDEN AP nicht - der
|
|
* behaelt seine Daten, bis er abgebaut und neu aufgebaut wird. */
|
|
static void reset_ap_defaults(void)
|
|
{
|
|
s_ap_ssid[0] = '\0';
|
|
copy(s_ap_pass, NINLIGHT_AP_PASSWORD, sizeof s_ap_pass);
|
|
s_ap_channel = 6;
|
|
}
|
|
|
|
/* Reisst den AP ab und raeumt dabei auch die Buchfuehrung auf. Laeuft nur im Netz-Task
|
|
* (oder vor dessen Start) und nie unter s_lock - clear_softap_param() fasst SDK-Zustand an. */
|
|
static void destroy_ap(void)
|
|
{
|
|
tls_wifi_softap_destroy();
|
|
s_ap_create_pending = false;
|
|
s_ap_live = false;
|
|
clear_softap_param(); /* C2: every softap teardown clears the persisted SOFTAP bit */
|
|
}
|
|
|
|
static void start_ap(void)
|
|
{
|
|
struct tls_softap_info_t ap;
|
|
struct tls_ip_info_t ip;
|
|
const char *ssid = s_ap_ssid[0] ? s_ap_ssid : s_hostname;
|
|
size_t n = strlen(s_ap_pass);
|
|
int r;
|
|
|
|
/* Steht derselbe AP schon, bleibt er stehen: die Zustandsmaschine verlangt start_ap
|
|
* auch dann, wenn sie den AP gar nicht erst abgebaut hat (fehlgeschlagener
|
|
* Onboarding-Versuch aus dem Standalone-Modus), und ein Neuaufbau wuerfe dort alle
|
|
* Clients ohne Not heraus. */
|
|
if (s_ap_live && strcmp(s_ap_live_ssid, ssid) == 0 && strcmp(s_ap_live_pass, s_ap_pass) == 0 &&
|
|
s_ap_live_channel == s_ap_channel)
|
|
return;
|
|
/* Umgekehrt: Name, Passwort oder Kanal haben sich geaendert, waehrend der AP steht.
|
|
* Ohne eigenen Abriss liefe er mit den alten Daten weiter. */
|
|
if (s_ap_live) {
|
|
printf("wifi: AP data changed, rebuilding\r\n");
|
|
destroy_ap();
|
|
}
|
|
|
|
memset(&ap, 0, sizeof ap);
|
|
memset(&ip, 0, sizeof ip);
|
|
copy((char *)ap.ssid, ssid, sizeof ap.ssid);
|
|
ap.encrypt = IEEE80211_ENCRYT_CCMP_WPA2;
|
|
ap.channel = s_ap_channel;
|
|
ap.keyinfo.format = 1; /* ascii */
|
|
ap.keyinfo.index = 1;
|
|
ap.keyinfo.key_len = (u8)n;
|
|
memcpy(ap.keyinfo.key, s_ap_pass, n);
|
|
ip.ip_addr[0] = 192; ip.ip_addr[1] = 168; ip.ip_addr[2] = 4; ip.ip_addr[3] = 1;
|
|
ip.netmask[0] = 255; ip.netmask[1] = 255; ip.netmask[2] = 255; ip.netmask[3] = 0;
|
|
memcpy(ip.dnsname, "local", 6);
|
|
r = tls_wifi_softap_create(&ap, &ip);
|
|
printf("wifi: AP %s (ch %u) %s\r\n", ssid, (unsigned)s_ap_channel,
|
|
r == WM_SUCCESS ? "up" : "failed");
|
|
/* M3: retry a failed AP create instead of leaving onboarding/fallback without an AP. */
|
|
s_ap_create_pending = r != WM_SUCCESS;
|
|
s_ap_create_retry_at = nl_app_now_ms() + 5000u;
|
|
s_ap_live = r == WM_SUCCESS;
|
|
if (s_ap_live) {
|
|
copy(s_ap_live_ssid, ssid, sizeof s_ap_live_ssid);
|
|
copy(s_ap_live_pass, s_ap_pass, sizeof s_ap_live_pass);
|
|
s_ap_live_channel = s_ap_channel;
|
|
s_ap_create_fails = 0;
|
|
return;
|
|
}
|
|
/* Nimmt das Funkteil diese Daten dauerhaft nicht an, steht die Lampe ohne AP da - und mit
|
|
* in den Einstellungen gespeicherten Wunschdaten auch nach einem Neustart wieder. Dann
|
|
* lieber ein AP unter unerwartetem Namen: der ist sichtbar und reparierbar. */
|
|
if (++s_ap_create_fails < NL_AP_CREATE_TRIES)
|
|
return;
|
|
s_ap_create_fails = 0;
|
|
if (s_ap_live_ssid[0] && (strcmp(s_ap_live_ssid, ssid) != 0 ||
|
|
strcmp(s_ap_live_pass, s_ap_pass) != 0 ||
|
|
s_ap_live_channel != s_ap_channel)) {
|
|
printf("wifi: AP create keeps failing, falling back to the last working data (%s, ch %u)\r\n",
|
|
s_ap_live_ssid, (unsigned)s_ap_live_channel);
|
|
copy(s_ap_ssid, s_ap_live_ssid, sizeof s_ap_ssid);
|
|
copy(s_ap_pass, s_ap_live_pass, sizeof s_ap_pass);
|
|
s_ap_channel = s_ap_live_channel;
|
|
} else if (s_ap_ssid[0] || s_ap_channel != 6 || strcmp(s_ap_pass, NINLIGHT_AP_PASSWORD) != 0) {
|
|
printf("wifi: AP create keeps failing, falling back to %s on ch 6\r\n", s_hostname);
|
|
reset_ap_defaults();
|
|
}
|
|
/* Sonst scheitern schon die Vorgaben - dann bleibt nur, es weiter zu versuchen. */
|
|
}
|
|
|
|
static void connect_station(bool test)
|
|
{
|
|
char ssid[NL_SSID_LEN], pass[NL_PASS_LEN];
|
|
int r;
|
|
|
|
lock();
|
|
copy(ssid, test ? s_test_ssid : s_ssid, sizeof ssid);
|
|
copy(pass, test ? s_test_pass : s_pass, sizeof pass);
|
|
unlock();
|
|
printf("wifi: connecting to %s\r\n", ssid);
|
|
r = tls_wifi_connect((u8 *)ssid, (u8)strlen(ssid), (u8 *)pass, (u8)strlen(pass));
|
|
if (r != WM_SUCCESS) /* M2: surface connect-call failures, not just later join events */
|
|
printf("wifi: tls_wifi_connect failed, err=%d\r\n", r);
|
|
}
|
|
|
|
static void pulse(bool on)
|
|
{
|
|
nl_app_lock();
|
|
nl_engine_pulse(on);
|
|
nl_app_unlock();
|
|
}
|
|
|
|
static void execute(nl_wifi_actions_t a)
|
|
{
|
|
if (a.save_test_creds) {
|
|
char ssid[NL_SSID_LEN], pass[NL_PASS_LEN];
|
|
lock();
|
|
copy(s_ssid, s_test_ssid, sizeof s_ssid);
|
|
copy(s_pass, s_test_pass, sizeof s_pass);
|
|
copy(ssid, s_ssid, sizeof ssid);
|
|
copy(pass, s_pass, sizeof pass);
|
|
unlock();
|
|
printf("wifi: onboarding ok, saving credentials for %s\r\n", ssid);
|
|
nl_app_set_wifi_credentials(ssid, pass);
|
|
}
|
|
if (a.clear_standalone) {
|
|
/* Sonst stuende die Lampe nach dem naechsten Neustart wieder in ihrem eigenen AP,
|
|
* obwohl sie gerade erfolgreich ins WLAN gewechselt ist. */
|
|
printf("wifi: standalone mode cleared\r\n");
|
|
/* Dieselbe Rueckstellung wie beim Zurueckschalten ueber die API: sonst kaeme ein
|
|
* spaeterer Rueckfall-AP (5 min ohne Station) mit Namen und Passwort des Nutzers
|
|
* hoch, obwohl standalone in den Einstellungen schon 0 ist. Gefahrlos, weil dieses
|
|
* Aktionsbuendel kein start_ap enthaelt - der noch laufende Standalone-AP bleibt
|
|
* unberuehrt, bis die Zustandsmaschine ihn abbaut. */
|
|
reset_ap_defaults();
|
|
nl_app_clear_standalone();
|
|
}
|
|
if (a.pulse_on)
|
|
pulse(true);
|
|
if (a.pulse_off)
|
|
pulse(false);
|
|
if (a.start_ap)
|
|
start_ap();
|
|
if (a.stop_ap) {
|
|
printf("wifi: AP down\r\n");
|
|
destroy_ap();
|
|
}
|
|
if (a.disconnect) {
|
|
printf("wifi: dropping stray/failed station attempt\r\n");
|
|
tls_wifi_disconnect();
|
|
}
|
|
if (a.connect)
|
|
connect_station(a.use_test_creds);
|
|
}
|
|
|
|
static nl_wifi_error_t join_error(void)
|
|
{
|
|
switch (tls_wifi_get_errno()) {
|
|
case WM_WIFI_EKEY: return NL_WIFI_ERR_AUTH;
|
|
case WM_WIFI_ENOAP: return NL_WIFI_ERR_NOT_FOUND;
|
|
default: return NL_WIFI_ERR_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
static void net_task(void *arg)
|
|
{
|
|
(void)arg;
|
|
for (;;) {
|
|
void *msg = NULL;
|
|
nl_wifi_actions_t ev, tick;
|
|
uint32_t m = 0, now;
|
|
bool got_ip_event = false, connected_now, ap_wanted, unexpected_softap,
|
|
standalone_ap_up;
|
|
bool recheck_ap = false;
|
|
|
|
memset(&ev, 0, sizeof ev);
|
|
if (tls_os_queue_receive(s_queue, &msg, 0, HZ / 10) == TLS_OS_SUCCESS)
|
|
m = (uint32_t)msg;
|
|
now = nl_app_now_ms();
|
|
|
|
lock();
|
|
if (m == (MSG_NETIF | NETIF_IP_NET_UP)) {
|
|
ev = nl_wifi_sm_event(&s_sm, NL_WIFI_EV_CONNECTED, NL_WIFI_ERR_NONE, now);
|
|
got_ip_event = true;
|
|
} else if (m == (MSG_NETIF | NETIF_WIFI_JOIN_FAILED)) {
|
|
ev = nl_wifi_sm_event(&s_sm, NL_WIFI_EV_JOIN_FAILED, join_error(), now);
|
|
} else if (m == (MSG_NETIF | NETIF_WIFI_DISCONNECTED)) {
|
|
ev = nl_wifi_sm_event(&s_sm, NL_WIFI_EV_DISCONNECTED, NL_WIFI_ERR_NONE, now);
|
|
} else if (m == MSG_ONBOARD) {
|
|
ev = nl_wifi_sm_onboard(&s_sm, now);
|
|
} else if (m == MSG_STANDALONE) {
|
|
/* Die Wunschwerte erst hier uebernehmen: der HTTP-Task hat sie abgelegt, der
|
|
* Netz-Task ist der einzige, der s_ap_* schreibt und start_ap() aufruft. */
|
|
if (s_req_enabled) {
|
|
copy(s_ap_ssid, s_req_ap_ssid, sizeof s_ap_ssid);
|
|
copy(s_ap_pass, s_req_ap_pass[0] ? s_req_ap_pass : NINLIGHT_AP_PASSWORD,
|
|
sizeof s_ap_pass);
|
|
s_ap_channel = s_req_ap_channel ? s_req_ap_channel : 6;
|
|
ev = nl_wifi_sm_standalone(&s_sm, s_req_pulse, now);
|
|
/* Steht der AP schon, liefert die Zustandsmaschine kein start_ap. Ob die
|
|
* neuen Daten einen Neuaufbau verlangen, entscheidet start_ap() selbst. */
|
|
recheck_ap = true;
|
|
} else {
|
|
/* Zurueck auf die Vorgaben: ein spaeterer Rueckfall- oder Onboarding-AP ist
|
|
* wieder der gewohnte "ninlight-<mac4>" mit dem einkompilierten Passwort,
|
|
* genau wie bei einer Lampe, die nie standalone war. Das greift erst beim
|
|
* NAECHSTEN Aufbau - der noch laufende AP behaelt Name und Passwort, bis ihn
|
|
* die Zustandsmaschine abbaut. */
|
|
reset_ap_defaults();
|
|
ev = nl_wifi_sm_leave_standalone(&s_sm, s_ssid[0] != '\0', now);
|
|
}
|
|
s_req_pending = false;
|
|
}
|
|
tick = nl_wifi_sm_tick(&s_sm, now);
|
|
/* I1: a late/stray IP-up must not announce mDNS unless the SM actually settled on STA_CONNECTED. */
|
|
connected_now = s_sm.state == NL_WIFI_ST_STA_CONNECTED;
|
|
ap_wanted = s_sm.ap_up;
|
|
/* C2 backstop: the SDK brought its own softap up behind our back (e.g. the clear in
|
|
* nl_wifi_start raced a pending sys_net_status_changed()) while our state machine
|
|
* does not want an AP. Only latch the fact here; tls_wifi_softap_destroy() and
|
|
* clear_softap_param() touch the SDK's own state and must not run while s_lock
|
|
* (which only guards s_sm and the credential buffers) is held. */
|
|
unexpected_softap = m == (MSG_NETIF | NETIF_WIFI_SOFTAP_SUCCESS) && !ap_wanted;
|
|
/* Nur die Lage merken; der Aufruf nach aussen folgt hinter der Entsperrung. */
|
|
standalone_ap_up = m == (MSG_NETIF | NETIF_WIFI_SOFTAP_SUCCESS) &&
|
|
s_sm.state == NL_WIFI_ST_STANDALONE;
|
|
unlock();
|
|
|
|
if (unexpected_softap) {
|
|
printf("wifi: unexpected SDK softap while AP should be down, destroying it\r\n");
|
|
destroy_ap();
|
|
}
|
|
|
|
if (m & MSG_NETIF)
|
|
printf("wifi: netif event %u\r\n", (unsigned)(m & 0xFFu));
|
|
execute(ev);
|
|
/* Nach einem Wechsel in den Standalone-Modus: start_ap() baut den AP neu auf, wenn
|
|
* Name, Passwort oder Kanal sich geaendert haben, und tut sonst nichts. */
|
|
if (recheck_ap && ap_wanted)
|
|
start_ap();
|
|
execute(tick);
|
|
if (got_ip_event && connected_now)
|
|
nl_mdns_on_ip_up();
|
|
/* Im eigenen AP gibt es kein Stationsereignis, an dem mDNS haengen koennte.
|
|
* Ohne diesen Aufruf findet die App ein Standalone-Geraet nicht - am
|
|
* 2026-10-01 am EVK gemessen: REST auf der AP-IP lief, ninlight-681f.local
|
|
* war weder aufloesbar noch sichtbar. */
|
|
if (standalone_ap_up)
|
|
nl_mdns_on_ip_up();
|
|
|
|
/* M3: retry a softap create that failed, as long as the SM still wants the AP up. */
|
|
if (s_ap_create_pending && ap_wanted && due_now(now, s_ap_create_retry_at)) {
|
|
printf("wifi: retrying AP create\r\n");
|
|
start_ap();
|
|
}
|
|
nl_net_beat++;
|
|
}
|
|
}
|
|
|
|
void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass,
|
|
const char *ap_ssid, uint8_t ap_channel, bool standalone, bool pulse)
|
|
{
|
|
nl_wifi_actions_t a;
|
|
struct netif *nif;
|
|
|
|
clear_softap_param(); /* C2: drop any softap bit left over from a previous onboarding/fallback */
|
|
|
|
tls_get_mac_addr(s_mac);
|
|
nl_names_hostname(s_mac, s_hostname, sizeof s_hostname);
|
|
copy(s_ssid, ssid, sizeof s_ssid);
|
|
copy(s_pass, pass, sizeof s_pass);
|
|
/* Wie in nl_wifi_request_standalone(): NULL ist hier zwar von keinem Aufrufer zu
|
|
* erwarten, aber copy() wuerde darueber stolpern. */
|
|
copy(s_ap_pass, ap_pass && ap_pass[0] ? ap_pass : NINLIGHT_AP_PASSWORD, sizeof s_ap_pass);
|
|
copy(s_ap_ssid, ap_ssid ? ap_ssid : "", sizeof s_ap_ssid); /* leer = Hostname */
|
|
s_ap_channel = ap_channel ? ap_channel : 6;
|
|
|
|
/* The SDK would otherwise reconnect on its own with credentials from its parameter area. */
|
|
nl_wifi_disable_sdk_auto_connect();
|
|
if (tls_wifi_softap_get_state() != WM_WIFI_DISCONNECTED) {
|
|
/* C2: the SDK already brought up its own softap (e.g. from NETIF_IP_NET_UP before
|
|
* we got a chance to run) - tear it down, our state machine owns AP lifecycle. */
|
|
printf("wifi: destroying SDK-created softap left over from boot\r\n");
|
|
destroy_ap();
|
|
}
|
|
nif = tls_get_netif();
|
|
if (nif)
|
|
nif->hostname = s_hostname; /* DHCP host name */
|
|
|
|
tls_os_mutex_create(0, &s_lock);
|
|
tls_os_queue_create(&s_queue, NL_NET_QUEUE);
|
|
tls_netif_add_status_event(netif_event);
|
|
|
|
lock();
|
|
a = nl_wifi_sm_start(&s_sm, s_ssid[0] != '\0', standalone, pulse, nl_app_now_ms());
|
|
unlock();
|
|
s_started = true;
|
|
printf("wifi: %s, hostname %s\r\n",
|
|
standalone ? "standalone AP" : (s_ssid[0] ? "station" : "onboarding AP"), s_hostname);
|
|
execute(a);
|
|
tls_os_task_create(NULL, "nl_net", net_task, NULL, (u8 *)s_stack, sizeof(s_stack), NL_NET_PRIO, 0);
|
|
}
|
|
|
|
int nl_wifi_request_onboard(const char *ssid, const char *pass)
|
|
{
|
|
bool busy;
|
|
|
|
if (!s_started)
|
|
return -1;
|
|
lock();
|
|
busy = s_sm.state == NL_WIFI_ST_ONBOARD_TEST;
|
|
if (!busy) {
|
|
copy(s_test_ssid, ssid, sizeof s_test_ssid);
|
|
copy(s_test_pass, pass, sizeof s_test_pass);
|
|
}
|
|
unlock();
|
|
if (busy) /* M1: API returns 503 "wifi busy" instead of queuing on top of a running test */
|
|
return -1;
|
|
return tls_os_queue_send(s_queue, (void *)MSG_ONBOARD, 0) == TLS_OS_SUCCESS ? 0 : -1;
|
|
}
|
|
|
|
int nl_wifi_request_standalone(bool enabled, const char *ssid, const char *pass,
|
|
uint8_t channel, bool pulse)
|
|
{
|
|
bool busy;
|
|
|
|
if (!s_started)
|
|
return -1;
|
|
lock();
|
|
/* Wie beim Onboarding: nicht auf einen laufenden Test draufsatteln, und keinen zweiten
|
|
* Wunsch annehmen, solange der erste noch in der Warteschlange steht - er ueberschriebe
|
|
* dessen Werte, die Antwort haette aber schon 202 gesagt. */
|
|
busy = s_sm.state == NL_WIFI_ST_ONBOARD_TEST || s_req_pending;
|
|
if (!busy) {
|
|
copy(s_req_ap_ssid, ssid ? ssid : "", sizeof s_req_ap_ssid);
|
|
copy(s_req_ap_pass, pass ? pass : "", sizeof s_req_ap_pass);
|
|
s_req_ap_channel = channel;
|
|
s_req_enabled = enabled;
|
|
s_req_pulse = pulse;
|
|
s_req_pending = true;
|
|
}
|
|
unlock();
|
|
if (busy)
|
|
return -1;
|
|
if (tls_os_queue_send(s_queue, (void *)MSG_STANDALONE, 0) != TLS_OS_SUCCESS) {
|
|
lock();
|
|
s_req_pending = false;
|
|
unlock();
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool nl_wifi_is_standalone(void)
|
|
{
|
|
bool r;
|
|
|
|
if (!s_started)
|
|
return false;
|
|
lock();
|
|
r = s_sm.state == NL_WIFI_ST_STANDALONE;
|
|
unlock();
|
|
return r;
|
|
}
|
|
|
|
bool nl_wifi_open_access(void)
|
|
{
|
|
bool r;
|
|
if (!s_started)
|
|
return false;
|
|
lock();
|
|
r = nl_wifi_sm_open_access(&s_sm);
|
|
unlock();
|
|
return r;
|
|
}
|
|
|
|
void nl_wifi_status(nl_wifi_status_t *out)
|
|
{
|
|
struct tls_ethif *e = tls_netif_get_ethif();
|
|
bool ap_up = false;
|
|
|
|
memset(out, 0, sizeof *out);
|
|
out->mode = "sta";
|
|
if (s_started) {
|
|
lock();
|
|
out->mode = nl_wifi_sm_mode(&s_sm);
|
|
out->error = nl_wifi_error_name(s_sm.error);
|
|
copy(out->ssid, s_ssid, sizeof out->ssid);
|
|
ap_up = s_sm.ap_up || s_sm.state == NL_WIFI_ST_AP_ONBOARDING;
|
|
unlock();
|
|
}
|
|
if (e && e->status) {
|
|
struct tls_curr_bss_t bss;
|
|
ip4addr_ntoa_r(ip_2_ip4(&e->ip_addr), out->sta_ip, sizeof out->sta_ip);
|
|
memset(&bss, 0, sizeof bss);
|
|
tls_wifi_get_current_bss(&bss);
|
|
/* W600 SDK reports RSSI magnitude as unsigned; EVK ninIOT measured ~-50 dBm as 50.
|
|
* Report negative for 0 < bss.rssi <= 127, unchanged for > 127 (already two's complement). */
|
|
out->rssi = (bss.rssi > 0 && bss.rssi <= 127) ? -(int)bss.rssi : (int)(int8_t)bss.rssi;
|
|
}
|
|
if (out->sta_ip[0])
|
|
copy(out->ip, out->sta_ip, sizeof out->ip);
|
|
else if (ap_up)
|
|
copy(out->ip, "192.168.4.1", sizeof out->ip);
|
|
}
|
|
|
|
const char *nl_wifi_hostname(void) { return s_hostname; }
|
|
|
|
void nl_wifi_mac(uint8_t mac[6]) { memcpy(mac, s_mac, 6); }
|
|
|
|
/* --- boot recovery: join a station directly, ahead of nl_wifi_start()/nl_wifi_sm --- */
|
|
|
|
static volatile bool s_recovery_join_done;
|
|
static volatile bool s_recovery_join_ok;
|
|
|
|
static void recovery_join_event(u8 status)
|
|
{
|
|
if (status == NETIF_IP_NET_UP) {
|
|
s_recovery_join_ok = true;
|
|
s_recovery_join_done = true;
|
|
} else if (status == NETIF_WIFI_JOIN_FAILED) {
|
|
s_recovery_join_done = true;
|
|
}
|
|
}
|
|
|
|
bool nl_wifi_join_blocking(const char *ssid, const char *pass, uint32_t timeout_ms)
|
|
{
|
|
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);
|
|
if (tls_wifi_connect((u8 *)ssid, (u8)strlen(ssid), (u8 *)pass, (u8)strlen(pass)) != WM_SUCCESS) {
|
|
tls_netif_remove_status_event(recovery_join_event);
|
|
return false;
|
|
}
|
|
while (!s_recovery_join_done && waited < timeout_ms) {
|
|
tls_watchdog_clr(); /* boot phase: called before app_task's own heartbeat gate exists */
|
|
tls_os_time_delay(HZ / 10);
|
|
waited += 100;
|
|
}
|
|
tls_netif_remove_status_event(recovery_join_event);
|
|
if (!s_recovery_join_ok) {
|
|
tls_wifi_disconnect(); /* cancel a stuck/timed-out join attempt */
|
|
return false;
|
|
}
|
|
/* I3 backstop: NETIF_IP_NET_UP carries no SSID, so an SDK-initiated join to its own stored AP
|
|
* would look exactly like ours - and the lamp would then fetch http://192.168.8.1/... from a
|
|
* foreign network. tls_wifi_get_current_bss() does report the SSID actually associated with
|
|
* (sdk/Include/WiFi/wm_wifi.h:190), so verify it before the caller starts a download. */
|
|
{
|
|
struct tls_curr_bss_t bss;
|
|
size_t n = strlen(ssid);
|
|
|
|
memset(&bss, 0, sizeof bss);
|
|
tls_wifi_get_current_bss(&bss);
|
|
if (bss.ssid_len != (u8)n || memcmp(bss.ssid, ssid, n) != 0) {
|
|
printf("wifi: joined a different network than %s, dropping the connection\r\n", ssid);
|
|
tls_wifi_disconnect();
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|