feat(wifi): Standalone-AP im Treiber und im Startpfad
This commit is contained in:
+140
-15
@@ -16,8 +16,12 @@
|
||||
|
||||
#define NL_NET_PRIO 35
|
||||
#define NL_NET_QUEUE 8
|
||||
#define MSG_NETIF 0x100u /* | netif status */
|
||||
#define MSG_ONBOARD 0x200u
|
||||
#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;
|
||||
@@ -29,6 +33,18 @@ 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;
|
||||
/* 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;
|
||||
|
||||
@@ -88,18 +104,43 @@ void nl_wifi_disable_sdk_auto_connect(void)
|
||||
tls_wifi_disconnect();
|
||||
}
|
||||
|
||||
/* 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, s_hostname, sizeof ap.ssid);
|
||||
copy((char *)ap.ssid, ssid, sizeof ap.ssid);
|
||||
ap.encrypt = IEEE80211_ENCRYT_CCMP_WPA2;
|
||||
ap.channel = 6;
|
||||
ap.channel = s_ap_channel;
|
||||
ap.keyinfo.format = 1; /* ascii */
|
||||
ap.keyinfo.index = 1;
|
||||
ap.keyinfo.key_len = (u8)n;
|
||||
@@ -108,10 +149,17 @@ static void start_ap(void)
|
||||
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 %s\r\n", s_hostname, r == WM_SUCCESS ? "up" : "failed");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void connect_station(bool test)
|
||||
@@ -149,6 +197,12 @@ static void execute(nl_wifi_actions_t a)
|
||||
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");
|
||||
nl_app_clear_standalone();
|
||||
}
|
||||
if (a.pulse_on)
|
||||
pulse(true);
|
||||
if (a.pulse_off)
|
||||
@@ -157,9 +211,7 @@ static void execute(nl_wifi_actions_t a)
|
||||
start_ap();
|
||||
if (a.stop_ap) {
|
||||
printf("wifi: AP down\r\n");
|
||||
tls_wifi_softap_destroy();
|
||||
s_ap_create_pending = false;
|
||||
clear_softap_param(); /* C2: every softap teardown clears the persisted SOFTAP bit */
|
||||
destroy_ap();
|
||||
}
|
||||
if (a.disconnect) {
|
||||
printf("wifi: dropping stray/failed station attempt\r\n");
|
||||
@@ -186,6 +238,7 @@ static void net_task(void *arg)
|
||||
nl_wifi_actions_t ev, tick;
|
||||
uint32_t m = 0, now;
|
||||
bool got_ip_event = false, connected_now, ap_wanted, unexpected_softap;
|
||||
bool recheck_ap = false;
|
||||
|
||||
memset(&ev, 0, sizeof ev);
|
||||
if (tls_os_queue_receive(s_queue, &msg, 0, HZ / 10) == TLS_OS_SUCCESS)
|
||||
@@ -202,6 +255,28 @@ static void net_task(void *arg)
|
||||
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. */
|
||||
s_ap_ssid[0] = '\0';
|
||||
copy(s_ap_pass, NINLIGHT_AP_PASSWORD, sizeof s_ap_pass);
|
||||
s_ap_channel = 6;
|
||||
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. */
|
||||
@@ -217,13 +292,16 @@ static void net_task(void *arg)
|
||||
|
||||
if (unexpected_softap) {
|
||||
printf("wifi: unexpected SDK softap while AP should be down, destroying it\r\n");
|
||||
tls_wifi_softap_destroy();
|
||||
clear_softap_param();
|
||||
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();
|
||||
@@ -237,7 +315,8 @@ static void net_task(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass)
|
||||
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;
|
||||
@@ -249,6 +328,8 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass)
|
||||
copy(s_ssid, ssid, sizeof s_ssid);
|
||||
copy(s_pass, pass, sizeof s_pass);
|
||||
copy(s_ap_pass, ap_pass, sizeof s_ap_pass);
|
||||
copy(s_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();
|
||||
@@ -256,8 +337,7 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass)
|
||||
/* 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");
|
||||
tls_wifi_softap_destroy();
|
||||
clear_softap_param();
|
||||
destroy_ap();
|
||||
}
|
||||
nif = tls_get_netif();
|
||||
if (nif)
|
||||
@@ -268,10 +348,11 @@ void nl_wifi_start(const char *ssid, const char *pass, const char *ap_pass)
|
||||
tls_netif_add_status_event(netif_event);
|
||||
|
||||
lock();
|
||||
a = nl_wifi_sm_start(&s_sm, s_ssid[0] != '\0', false, false, nl_app_now_ms());
|
||||
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", s_ssid[0] ? "station" : "onboarding AP", s_hostname);
|
||||
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);
|
||||
}
|
||||
@@ -294,6 +375,50 @@ int nl_wifi_request_onboard(const char *ssid, const char *pass)
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user