feat: WLAN-Zustandsautomat mit Onboarding, Backoff und AP-Fallback
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
#include "net/nl_wifi_sm.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static bool due(uint32_t now, uint32_t at)
|
||||
{
|
||||
return (int32_t)(now - at) >= 0;
|
||||
}
|
||||
|
||||
static nl_wifi_actions_t none(void)
|
||||
{
|
||||
nl_wifi_actions_t a;
|
||||
memset(&a, 0, sizeof a);
|
||||
return a;
|
||||
}
|
||||
|
||||
static void schedule_retry(nl_wifi_sm_t *sm, uint32_t now)
|
||||
{
|
||||
sm->retry_armed = true;
|
||||
sm->retry_at = now + sm->backoff;
|
||||
sm->backoff = sm->backoff * 2u > NL_WIFI_BACKOFF_MAX_MS ? NL_WIFI_BACKOFF_MAX_MS : sm->backoff * 2u;
|
||||
}
|
||||
|
||||
static void begin_connecting(nl_wifi_sm_t *sm, uint32_t now)
|
||||
{
|
||||
sm->state = NL_WIFI_ST_STA_CONNECTING;
|
||||
sm->since = now;
|
||||
sm->backoff = NL_WIFI_BACKOFF_MIN_MS;
|
||||
sm->retry_armed = false;
|
||||
}
|
||||
|
||||
static void arm_ap_stop(nl_wifi_sm_t *sm, uint32_t at)
|
||||
{
|
||||
if (sm->ap_up) {
|
||||
sm->ap_stop_armed = true;
|
||||
sm->ap_stop_at = at;
|
||||
}
|
||||
}
|
||||
|
||||
static void fail_test(nl_wifi_sm_t *sm, nl_wifi_error_t err, uint32_t now, nl_wifi_actions_t *a)
|
||||
{
|
||||
sm->error = err;
|
||||
if (sm->have_creds) {
|
||||
sm->state = NL_WIFI_ST_APSTA_FALLBACK;
|
||||
sm->since = now;
|
||||
sm->backoff = NL_WIFI_BACKOFF_MIN_MS;
|
||||
sm->retry_armed = false;
|
||||
a->connect = true; /* old credentials */
|
||||
} else {
|
||||
sm->state = NL_WIFI_ST_AP_ONBOARDING;
|
||||
}
|
||||
}
|
||||
|
||||
nl_wifi_actions_t nl_wifi_sm_start(nl_wifi_sm_t *sm, bool have_creds, uint32_t now)
|
||||
{
|
||||
nl_wifi_actions_t a = none();
|
||||
|
||||
memset(sm, 0, sizeof *sm);
|
||||
sm->have_creds = have_creds;
|
||||
if (have_creds) {
|
||||
begin_connecting(sm, now);
|
||||
a.connect = true;
|
||||
} else {
|
||||
sm->state = NL_WIFI_ST_AP_ONBOARDING;
|
||||
sm->ap_up = true;
|
||||
sm->pulsing = true;
|
||||
a.start_ap = true;
|
||||
a.pulse_on = true;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
nl_wifi_actions_t nl_wifi_sm_event(nl_wifi_sm_t *sm, nl_wifi_event_t ev, nl_wifi_error_t err, uint32_t now)
|
||||
{
|
||||
nl_wifi_actions_t a = none();
|
||||
|
||||
switch (sm->state) {
|
||||
case NL_WIFI_ST_STA_CONNECTING:
|
||||
case NL_WIFI_ST_APSTA_FALLBACK:
|
||||
if (ev == NL_WIFI_EV_CONNECTED) {
|
||||
sm->state = NL_WIFI_ST_STA_CONNECTED;
|
||||
sm->retry_armed = false;
|
||||
sm->backoff = NL_WIFI_BACKOFF_MIN_MS;
|
||||
arm_ap_stop(sm, now + NL_WIFI_AP_CLOSE_FALLBACK_MS);
|
||||
} else {
|
||||
schedule_retry(sm, now);
|
||||
}
|
||||
break;
|
||||
case NL_WIFI_ST_STA_CONNECTED:
|
||||
if (ev != NL_WIFI_EV_CONNECTED) {
|
||||
begin_connecting(sm, now);
|
||||
schedule_retry(sm, now);
|
||||
}
|
||||
break;
|
||||
case NL_WIFI_ST_ONBOARD_TEST:
|
||||
if (ev == NL_WIFI_EV_CONNECTED) {
|
||||
a.save_test_creds = true;
|
||||
sm->have_creds = true;
|
||||
sm->error = NL_WIFI_ERR_NONE;
|
||||
sm->state = NL_WIFI_ST_STA_CONNECTED;
|
||||
sm->backoff = NL_WIFI_BACKOFF_MIN_MS;
|
||||
arm_ap_stop(sm, now + NL_WIFI_AP_CLOSE_ONBOARD_MS);
|
||||
if (sm->pulsing) {
|
||||
sm->pulsing = false;
|
||||
a.pulse_off = true;
|
||||
}
|
||||
} else if (ev == NL_WIFI_EV_JOIN_FAILED) {
|
||||
fail_test(sm, err == NL_WIFI_ERR_NONE ? NL_WIFI_ERR_TIMEOUT : err, now, &a);
|
||||
}
|
||||
break;
|
||||
case NL_WIFI_ST_AP_ONBOARDING:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
nl_wifi_actions_t nl_wifi_sm_tick(nl_wifi_sm_t *sm, uint32_t now)
|
||||
{
|
||||
nl_wifi_actions_t a = none();
|
||||
|
||||
if (sm->ap_stop_armed && due(now, sm->ap_stop_at)) {
|
||||
sm->ap_stop_armed = false;
|
||||
sm->ap_up = false;
|
||||
a.stop_ap = true;
|
||||
}
|
||||
if (sm->state == NL_WIFI_ST_STA_CONNECTING && due(now, sm->since + NL_WIFI_FALLBACK_MS)) {
|
||||
sm->state = NL_WIFI_ST_APSTA_FALLBACK;
|
||||
if (!sm->ap_up) {
|
||||
sm->ap_up = true;
|
||||
a.start_ap = true;
|
||||
}
|
||||
}
|
||||
if ((sm->state == NL_WIFI_ST_STA_CONNECTING || sm->state == NL_WIFI_ST_APSTA_FALLBACK) && sm->retry_armed &&
|
||||
due(now, sm->retry_at)) {
|
||||
sm->retry_armed = false;
|
||||
a.connect = true;
|
||||
}
|
||||
if (sm->state == NL_WIFI_ST_ONBOARD_TEST && due(now, sm->deadline))
|
||||
fail_test(sm, NL_WIFI_ERR_TIMEOUT, now, &a);
|
||||
return a;
|
||||
}
|
||||
|
||||
nl_wifi_actions_t nl_wifi_sm_onboard(nl_wifi_sm_t *sm, uint32_t now)
|
||||
{
|
||||
nl_wifi_actions_t a = none();
|
||||
|
||||
if (!sm->ap_up) {
|
||||
sm->ap_up = true;
|
||||
a.start_ap = true;
|
||||
}
|
||||
sm->state = NL_WIFI_ST_ONBOARD_TEST;
|
||||
sm->error = NL_WIFI_ERR_NONE;
|
||||
sm->deadline = now + NL_WIFI_ONBOARD_TIMEOUT_MS;
|
||||
sm->retry_armed = false;
|
||||
sm->ap_stop_armed = false;
|
||||
a.connect = true;
|
||||
a.use_test_creds = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
bool nl_wifi_sm_open_access(const nl_wifi_sm_t *sm)
|
||||
{
|
||||
return sm->state == NL_WIFI_ST_AP_ONBOARDING || (sm->state == NL_WIFI_ST_ONBOARD_TEST && !sm->have_creds);
|
||||
}
|
||||
|
||||
const char *nl_wifi_sm_mode(const nl_wifi_sm_t *sm)
|
||||
{
|
||||
if (sm->state == NL_WIFI_ST_AP_ONBOARDING)
|
||||
return "ap";
|
||||
return sm->ap_up ? "apsta" : "sta";
|
||||
}
|
||||
|
||||
const char *nl_wifi_error_name(nl_wifi_error_t e)
|
||||
{
|
||||
switch (e) {
|
||||
case NL_WIFI_ERR_AUTH: return "auth";
|
||||
case NL_WIFI_ERR_NOT_FOUND: return "not_found";
|
||||
case NL_WIFI_ERR_TIMEOUT: return "timeout";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef NL_WIFI_SM_H
|
||||
#define NL_WIFI_SM_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define NL_WIFI_FALLBACK_MS 300000u /* 5 min without connection -> AP+STA */
|
||||
#define NL_WIFI_ONBOARD_TIMEOUT_MS 30000u
|
||||
#define NL_WIFI_AP_CLOSE_ONBOARD_MS 60000u
|
||||
#define NL_WIFI_AP_CLOSE_FALLBACK_MS 10000u
|
||||
#define NL_WIFI_BACKOFF_MIN_MS 1000u
|
||||
#define NL_WIFI_BACKOFF_MAX_MS 60000u
|
||||
|
||||
typedef enum {
|
||||
NL_WIFI_ST_AP_ONBOARDING = 0,
|
||||
NL_WIFI_ST_STA_CONNECTING,
|
||||
NL_WIFI_ST_STA_CONNECTED,
|
||||
NL_WIFI_ST_APSTA_FALLBACK,
|
||||
NL_WIFI_ST_ONBOARD_TEST
|
||||
} nl_wifi_state_t;
|
||||
|
||||
typedef enum { NL_WIFI_ERR_NONE = 0, NL_WIFI_ERR_AUTH, NL_WIFI_ERR_NOT_FOUND, NL_WIFI_ERR_TIMEOUT } nl_wifi_error_t;
|
||||
typedef enum { NL_WIFI_EV_CONNECTED = 0, NL_WIFI_EV_JOIN_FAILED, NL_WIFI_EV_DISCONNECTED } nl_wifi_event_t;
|
||||
|
||||
/* What the driver has to do after a state machine call. */
|
||||
typedef struct {
|
||||
bool start_ap;
|
||||
bool stop_ap;
|
||||
bool connect; /* connect the station ... */
|
||||
bool use_test_creds; /* ... with the onboarding credentials instead of the stored ones */
|
||||
bool save_test_creds; /* onboarding succeeded: persist the onboarding credentials */
|
||||
bool pulse_on;
|
||||
bool pulse_off;
|
||||
} nl_wifi_actions_t;
|
||||
|
||||
typedef struct {
|
||||
nl_wifi_state_t state;
|
||||
nl_wifi_error_t error; /* result of the last onboarding test */
|
||||
bool have_creds;
|
||||
bool ap_up;
|
||||
bool pulsing;
|
||||
bool retry_armed;
|
||||
bool ap_stop_armed;
|
||||
uint32_t since; /* start of the current connecting phase */
|
||||
uint32_t retry_at;
|
||||
uint32_t backoff;
|
||||
uint32_t deadline; /* onboarding test */
|
||||
uint32_t ap_stop_at;
|
||||
} nl_wifi_sm_t;
|
||||
|
||||
nl_wifi_actions_t nl_wifi_sm_start(nl_wifi_sm_t *sm, bool have_creds, uint32_t now);
|
||||
nl_wifi_actions_t nl_wifi_sm_event(nl_wifi_sm_t *sm, nl_wifi_event_t ev, nl_wifi_error_t err, uint32_t now);
|
||||
nl_wifi_actions_t nl_wifi_sm_tick(nl_wifi_sm_t *sm, uint32_t now);
|
||||
nl_wifi_actions_t nl_wifi_sm_onboard(nl_wifi_sm_t *sm, uint32_t now);
|
||||
bool nl_wifi_sm_open_access(const nl_wifi_sm_t *sm);
|
||||
const char *nl_wifi_sm_mode(const nl_wifi_sm_t *sm);
|
||||
const char *nl_wifi_error_name(nl_wifi_error_t e);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user