Files
ninlight-w600/src/net/nl_wifi_w600.c
T
Michael Sauer 8805aa764d fix: Härtungen aus dem Plan-2-Review
- wm_internal_fls.c: TLS_DBGPRT_ERR ist in diesem Build wegkompiliert, daher
  printf() für die fehlgeschlagene s_fls2_lock-Anlage; PATCHES.md korrigiert.
- nl_wifi_w600.c: der SoftAP-Backstop (Zerstören eines unerwarteten SDK-Softap)
  lief bisher innerhalb von lock()/unlock() - tls_wifi_softap_destroy() und
  clear_softap_param() greifen aber auf SDK-Zustand zu, nicht auf s_sm/die
  Credential-Puffer, die s_lock eigentlich schützt. Jetzt wird im Lock nur noch
  ein lokales Flag gesetzt, die SDK-Aufrufe laufen danach.
- clear_softap_param() liest TLS_PARAM_ID_WPROTOCOL jetzt aus SRAM (FALSE)
  statt Flash (TRUE) - das Schreiben bleibt weiterhin geflasht.
- app_main.c füttert den Watchdog erst, wenn Licht-, Netz- und HTTP-Task
  jeweils seit dem letzten Feed einen Fortschritt gemeldet haben. Dafür
  bekommen nl_wifi_w600.c (nl_net_beat) und nl_http_server.c (nl_http_beat)
  je einen volatile Heartbeat-Zähler; der HTTP-Listen-Socket bekommt ein
  SO_RCVTIMEO von 2 s auf accept(), damit er auch ohne Anfragen tickt.
2026-09-12 00:59:40 +02:00

327 lines
11 KiB
C

#include <stdio.h>
#include <string.h>
#include "wm_include.h"
#include "wm_efuse.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
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 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);
}
}
static void start_ap(void)
{
struct tls_softap_info_t ap;
struct tls_ip_info_t ip;
size_t n = strlen(s_ap_pass);
int r;
memset(&ap, 0, sizeof ap);
memset(&ip, 0, sizeof ip);
copy((char *)ap.ssid, s_hostname, sizeof ap.ssid);
ap.encrypt = IEEE80211_ENCRYT_CCMP_WPA2;
ap.channel = 6;
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 %s\r\n", s_hostname, 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;
}
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.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");
tls_wifi_softap_destroy();
s_ap_create_pending = false;
clear_softap_param(); /* C2: every softap teardown clears the persisted SOFTAP bit */
}
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;
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);
}
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;
unlock();
if (unexpected_softap) {
printf("wifi: unexpected SDK softap while AP should be down, destroying it\r\n");
tls_wifi_softap_destroy();
clear_softap_param();
}
if (m & MSG_NETIF)
printf("wifi: netif event %u\r\n", (unsigned)(m & 0xFFu));
execute(ev);
execute(tick);
if (got_ip_event && connected_now)
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)
{
u8 auto_mode = WIFI_AUTO_CNT_OFF;
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);
copy(s_ap_pass, ap_pass, sizeof s_ap_pass);
/* The SDK would otherwise reconnect on its own with credentials from its parameter area. */
tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_GET, &auto_mode);
if (auto_mode != WIFI_AUTO_CNT_OFF) {
auto_mode = WIFI_AUTO_CNT_OFF;
tls_wifi_auto_connect_flag(WIFI_AUTO_CNT_FLAG_SET, &auto_mode);
}
tls_wifi_disconnect();
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");
tls_wifi_softap_destroy();
clear_softap_param();
}
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', nl_app_now_ms());
unlock();
s_started = true;
printf("wifi: %s, hostname %s\r\n", 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;
}
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); }