From 1bade1ded2651e87e7cf9840456f0036bea91373 Mon Sep 17 00:00:00 2001 From: Marcin Niestroj Date: Fri, 6 Oct 2023 14:54:28 +0200 Subject: [PATCH] samples: implement WiFi state machine to handle reconnects Implement WiFi connectivity state machine running in a dedicated work queue (which runs in a dedicated thread). Network management events from Zephyr are handled there, as well as timeouts and retries when attempting to connect. As a result, WiFi connectivity is bringed back whenever there was a temporary problem with WiFi AP connection. In case WiFi radio is not ready (L1 is down, which results in LOWER_DOWN interface management event/state) wait until it becomes ready (which is required by ESP32 WiFi driver, since it does not handle connect requests otherwise). Initialize and launch WiFi work queue thread automatically by using SYS_INIT(), so that `net_connect()` function does not need to handle WiFi explicitly. Since `net_connect()` handles mostly DHCPv4 setup and waiting to obtain IPv4 address, there is no need to check whether interace is UP at the beginning. DHCPv4 in Zephyr has its own state machine, with network events handling, all it requires is just calling net_dhcpv4_start() even for interfaces that are not operational at the time of function call. Replace waiting for "DHCPv4 bound" event with waiting for "IPv4 address add" event. The latter allows to wait for WiFi to be connected to AP with assigned IP address, while still handling (implicitly) "DHCPv4 bound" event, which is required for ethernet cable connected devices. Signed-off-by: Marcin Niestroj --- samples/common/Kconfig | 16 + samples/common/include/samples/common/wifi.h | 22 - samples/common/net_connect.c | 48 +- samples/common/wifi.c | 483 ++++++++++++++++--- 4 files changed, 443 insertions(+), 126 deletions(-) delete mode 100644 samples/common/include/samples/common/wifi.h diff --git a/samples/common/Kconfig b/samples/common/Kconfig index 41ef5b7a..e30d35ba 100644 --- a/samples/common/Kconfig +++ b/samples/common/Kconfig @@ -103,6 +103,22 @@ config GOLIOTH_SAMPLE_WIFI help Enable utilities for easy WiFi setup, mainly for use inside samples/. +if GOLIOTH_SAMPLE_WIFI + +config GOLIOTH_SAMPLE_WIFI_STACK_SIZE + int "WiFi manager stack size" + default 2048 + +config GOLIOTH_SAMPLE_WIFI_INIT_PRIORITY + int "WiFi manager init priority" + default 90 + +config GOLIOTH_SAMPLE_WIFI_THREAD_PRIORITY + int "WiFi manager thread priority" + default 7 + +endif # GOLIOTH_SAMPLE_WIFI + config GOLIOTH_SAMPLE_WIFI_SETTINGS bool "Load SSID and PSK from settigs subsystem" depends on GOLIOTH_SAMPLE_WIFI diff --git a/samples/common/include/samples/common/wifi.h b/samples/common/include/samples/common/wifi.h deleted file mode 100644 index 47babf32..00000000 --- a/samples/common/include/samples/common/wifi.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2021 Golioth, Inc. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef __GOLIOTH_INCLUDE_GOLIOTH_WIFI_H__ -#define __GOLIOTH_INCLUDE_GOLIOTH_WIFI_H__ - -#include - -/** - * @defgroup wifi Golioth Wifi - * @ingroup net - * @{ - */ - -void wifi_connect(struct net_if *iface); - -/** @} */ - -#endif /* __GOLIOTH_INCLUDE_GOLIOTH_WIFI_H__ */ diff --git a/samples/common/net_connect.c b/samples/common/net_connect.c index b94cd2ee..d5dba70e 100644 --- a/samples/common/net_connect.c +++ b/samples/common/net_connect.c @@ -8,7 +8,6 @@ LOG_MODULE_REGISTER(golioth_samples, LOG_LEVEL_DBG); #include -#include #include #include @@ -28,65 +27,30 @@ static void event_cb_handler(struct net_mgmt_event_callback *cb, } } -static void wait_for_iface_up_event(struct net_if *iface) +static void wait_for_net_event(struct net_if *iface, uint32_t event) { struct wait_data wait; wait.cb.handler = event_cb_handler; - wait.cb.event_mask = NET_EVENT_IF_UP; + wait.cb.event_mask = event; k_sem_init(&wait.sem, 0, 1); net_mgmt_add_event_callback(&wait.cb); - if (!net_if_is_up(iface)) { - k_sem_take(&wait.sem, K_FOREVER); - } + k_sem_take(&wait.sem, K_FOREVER); net_mgmt_del_event_callback(&wait.cb); } -static void wait_for_iface_up_poll(struct net_if *iface) -{ - while (!net_if_is_up(iface)) { - LOG_DBG("Interface is not up yet!"); - k_sleep(K_MSEC(100)); - } -} - -static void wait_for_iface_up(struct net_if *iface) -{ - if (IS_ENABLED(CONFIG_NET_MGMT_EVENT)) { - wait_for_iface_up_event(iface); - } else { - wait_for_iface_up_poll(iface); - } -} - -static void wait_for_dhcp_bound(struct net_if *iface) -{ - (void)net_mgmt_event_wait_on_iface(iface, NET_EVENT_IPV4_DHCP_BOUND, - NULL, NULL, NULL, - K_FOREVER); -} - void net_connect(void) { struct net_if *iface = net_if_get_default(); - if (!(IS_ENABLED(CONFIG_GOLIOTH_SAMPLE_WIFI) && - net_if_flag_is_set(iface, NET_IF_DORMANT))) { - LOG_INF("Waiting for interface to be up"); - wait_for_iface_up(iface); - } - - if (IS_ENABLED(CONFIG_GOLIOTH_SAMPLE_WIFI)) { - LOG_INF("Connecting to WiFi"); - wifi_connect(iface); - } - if (IS_ENABLED(CONFIG_GOLIOTH_SAMPLE_DHCP_BIND)) { LOG_INF("Starting DHCP to obtain IP address"); net_dhcpv4_start(iface); - wait_for_dhcp_bound(iface); } + + LOG_INF("Waiting to obtain IP address"); + wait_for_net_event(iface, NET_EVENT_IPV4_ADDR_ADD); } diff --git a/samples/common/wifi.c b/samples/common/wifi.c index c566eee2..dc209cc5 100644 --- a/samples/common/wifi.c +++ b/samples/common/wifi.c @@ -1,44 +1,143 @@ /* - * Copyright (c) 2021 Golioth, Inc. + * Copyright (c) 2021-2023 Golioth, Inc. * * SPDX-License-Identifier: Apache-2.0 */ #include -LOG_MODULE_REGISTER(golioth_wifi, LOG_LEVEL_DBG); +LOG_MODULE_REGISTER(golioth_wifi, LOG_LEVEL_INF); -#include +#include +#include +#include #include +#include #include +#include -struct wifi_data { +#define RESET_AFTER_DISCONNECT_AND_EMPTY_SCAN_RESULT \ + IS_ENABLED(CONFIG_WIFI_MANAGER_RESET_AFTER_DISCONNECT_AND_EMPTY_SCAN_RESULT) + +#define WIFI_MANAGER_MGMT_EVENTS (NET_EVENT_WIFI_CONNECT_RESULT | \ + NET_EVENT_WIFI_DISCONNECT_RESULT) + +#define NET_STATE_WIFI_CONNECTED BIT(0) +#define NET_STATE_WIFI_CONNECTING BIT(1) +#define NET_STATE_IPV4_READY BIT(2) + +enum wifi_state { + WIFI_STATE_IDLE, + WIFI_STATE_WAITING_FOR_LOWER_UP, + WIFI_STATE_CONNECTING, + WIFI_STATE_DISCONNECTING, + WIFI_STATE_READY, + WIFI_STATE_LAST = WIFI_STATE_READY, + WIFI_STATE_INVALID, + WIFI_STATE_WAIT, +}; + +enum wifi_event { + WIFI_EVENT_START, + WIFI_EVENT_LOWER_UP, + WIFI_EVENT_CONNECTED, + WIFI_EVENT_DISCONNECTED, + WIFI_EVENT_IP_ADD, + WIFI_EVENT_IP_DEL, +}; + +struct wifi_manager_config { + k_thread_stack_t *workq_stack; + size_t workq_stack_size; + struct k_work_queue_config workq_config; +}; + +enum wifi_manager_flags { + WIFI_FLAG_ENABLE, + WIFI_FLAG_RECONNECT, +}; + +struct wifi_manager_data { + struct net_if *iface; + + struct k_work_q workq; + enum wifi_state state; + + atomic_t net_state; + struct k_work net_mgmt_event_work; + int net_state_old; + + atomic_t flags; + + enum wifi_event event; + struct k_work event_work; + + struct net_mgmt_event_callback ipv4_mgmt_cb; struct net_mgmt_event_callback wifi_mgmt_cb; - struct k_sem connect_sem; - int connect_status; }; -static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, - uint32_t mgmt_event, struct net_if *iface) +static const char *wifi_state_str(enum wifi_state state) { - struct wifi_data *wifi = CONTAINER_OF(cb, struct wifi_data, wifi_mgmt_cb); - const struct wifi_status *status = cb->info; - - switch (mgmt_event) { - case NET_EVENT_WIFI_CONNECT_RESULT: - LOG_INF("%s with status: %d", "Connected", status->status); - wifi->connect_status = status->status; - k_sem_give(&wifi->connect_sem); - break; - case NET_EVENT_WIFI_DISCONNECT_RESULT: - LOG_INF("%s with status: %d", "Disconnected", status->status); - wifi->connect_status = -EFAULT; - k_sem_give(&wifi->connect_sem); - break; + switch (state) { + case WIFI_STATE_IDLE: + return "IDLE"; + case WIFI_STATE_WAITING_FOR_LOWER_UP: + return "WAIT_FOR_LOWER_UP"; + case WIFI_STATE_CONNECTING: + return "CONNECTING"; + case WIFI_STATE_DISCONNECTING: + return "DISCONNECTING"; + case WIFI_STATE_READY: + return "READY"; + case WIFI_STATE_INVALID: + return "INVALID"; + case WIFI_STATE_WAIT: default: - break; + return "WAIT"; } + + return ""; } +static const char *wifi_event_str(enum wifi_event event) +{ + switch (event) { + case WIFI_EVENT_START: + return "START"; + case WIFI_EVENT_LOWER_UP: + return "LOWER_UP"; + case WIFI_EVENT_CONNECTED: + return "CONNECTED"; + case WIFI_EVENT_DISCONNECTED: + return "DISCONNECTED"; + case WIFI_EVENT_IP_ADD: + return "IP_ADD"; + case WIFI_EVENT_IP_DEL: + return "IP_DEL"; + } + + return ""; +} + +#define MAP_OFFSET 1 + +#define IDL (WIFI_STATE_IDLE + MAP_OFFSET) +#define LUP (WIFI_STATE_WAITING_FOR_LOWER_UP + MAP_OFFSET) +#define CON (WIFI_STATE_CONNECTING + MAP_OFFSET) +#define DIS (WIFI_STATE_DISCONNECTING + MAP_OFFSET) +#define RDY (WIFI_STATE_READY + MAP_OFFSET) +#define INV (WIFI_STATE_INVALID + MAP_OFFSET) +#define WAIT(ms) (WIFI_STATE_WAIT + MAP_OFFSET + (ms)) + +static const int wifi_state_change_map[][WIFI_STATE_LAST + 1] = { + /* IDL, LUP, CON, DIS, RDY */ + [WIFI_EVENT_START] = { LUP, WAIT(100), INV, INV, INV }, + [WIFI_EVENT_LOWER_UP] = { INV, CON, INV, INV, INV }, + [WIFI_EVENT_CONNECTED] = { INV, INV, 0, WAIT(3000), 0 }, + [WIFI_EVENT_DISCONNECTED] = { INV, INV, WAIT(1000), IDL, CON }, + [WIFI_EVENT_IP_ADD] = { INV, INV, RDY, RDY, 0 }, + [WIFI_EVENT_IP_DEL] = { INV, INV, 0, 0, CON }, +}; + #if defined(CONFIG_GOLIOTH_SAMPLE_WIFI_SETTINGS) static uint8_t wifi_ssid[WIFI_SSID_MAX_LEN]; @@ -117,7 +216,34 @@ static size_t wifi_psk_len = sizeof(CONFIG_GOLIOTH_SAMPLE_WIFI_PSK) - 1; #endif /* defined(CONFIG_GOLIOTH_SAMPLE_WIFI_SETTINGS) */ -void wifi_connect(struct net_if *iface) +static void wifi_event_notify(struct wifi_manager_data *wifi_mgmt, + enum wifi_event event) +{ + wifi_mgmt->event = event; + k_work_submit_to_queue(&wifi_mgmt->workq, &wifi_mgmt->event_work); +} + +/** + * @brief Update CONNECTING flag from wifi_mgmt workqueue + * + * @note This function needs to be used only from within workqueue, as it + * accesses both atomic 'net_state' and unprotected 'net_state_old'. + * 'net_state_old' is altered to make clear that CONNECTING state is up-to-date + * with workqueue processing logic. + */ +static void wifi_mgmt_connecting_update(struct wifi_manager_data *wifi_mgmt, + bool connecting) +{ + if (connecting) { + atomic_or(&wifi_mgmt->net_state, NET_STATE_WIFI_CONNECTING); + wifi_mgmt->net_state_old |= NET_STATE_WIFI_CONNECTING; + } else { + atomic_and(&wifi_mgmt->net_state, ~NET_STATE_WIFI_CONNECTING); + wifi_mgmt->net_state_old &= ~NET_STATE_WIFI_CONNECTING; + } +} + +static void wifi_connect(struct wifi_manager_data *wifi_mgmt) { struct wifi_connect_req_params params = { .ssid = wifi_ssid, @@ -128,54 +254,287 @@ void wifi_connect(struct net_if *iface) .security = wifi_psk_len > 0 ? WIFI_SECURITY_TYPE_PSK : WIFI_SECURITY_TYPE_NONE, }; - struct wifi_data wifi; int err; - int attempts = 10; - int timeout_msec = 500; - k_sem_init(&wifi.connect_sem, 0, 1); + LOG_INF("Connecting to '%.*s'", wifi_ssid_len, wifi_ssid); - net_mgmt_init_event_callback(&wifi.wifi_mgmt_cb, - wifi_mgmt_event_handler, - (NET_EVENT_WIFI_CONNECT_RESULT | - NET_EVENT_WIFI_DISCONNECT_RESULT)); - net_mgmt_add_event_callback(&wifi.wifi_mgmt_cb); - - while (attempts--) { - err = net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, - ¶ms, sizeof(params)); - if (err == -EALREADY) { - LOG_INF("Already connected to WiFi"); - break; - } else if (err) { - LOG_ERR("Failed to request WiFi connect: %d", err); - goto retry; - } + wifi_mgmt_connecting_update(wifi_mgmt, true); - /* wait for notification from connect request */ - err = k_sem_take(&wifi.connect_sem, K_SECONDS(20)); - if (err) { - LOG_ERR("Timeout waiting for connection with WiFi"); - goto retry; + err = net_mgmt(NET_REQUEST_WIFI_CONNECT, wifi_mgmt->iface, + ¶ms, sizeof(params)); + if (err == -EALREADY) { + LOG_INF("already connected"); + + wifi_mgmt_connecting_update(wifi_mgmt, false); + + if (atomic_test_and_clear_bit(&wifi_mgmt->flags, WIFI_FLAG_RECONNECT)) { + LOG_INF("reconnecting"); + + err = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, wifi_mgmt->iface, + NULL, 0); + if (err) { + LOG_ERR("failed to request disconnect: %d", err); + goto handle_err; + } + } else { + wifi_event_notify(wifi_mgmt, WIFI_EVENT_CONNECTED); } + } else if (err) { + LOG_ERR("failed to request connect: %d", err); + goto handle_err; + } + + return; + +handle_err: + wifi_mgmt_connecting_update(wifi_mgmt, false); + wifi_event_notify(wifi_mgmt, WIFI_EVENT_DISCONNECTED); +} + +static void wifi_disconnect(struct wifi_manager_data *wifi_mgmt) +{ + int err; + + err = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, wifi_mgmt->iface, NULL, 0); + if (err) { + LOG_ERR("failed to request disconnect: %d", err); + wifi_event_notify(wifi_mgmt, WIFI_EVENT_DISCONNECTED); + } +} + +static void wifi_check_lower_up(struct wifi_manager_data *wifi_mgmt) +{ + if (net_if_flag_is_set(wifi_mgmt->iface, NET_IF_LOWER_UP)) { + wifi_event_notify(wifi_mgmt, WIFI_EVENT_LOWER_UP); + } else { + wifi_event_notify(wifi_mgmt, WIFI_EVENT_START); + } +} + +static void wifi_ready(struct wifi_manager_data *wifi_mgmt) +{ + LOG_DBG("ready"); +} + +static void wifi_state_change(struct wifi_manager_data *wifi_mgmt, + enum wifi_state state) +{ + LOG_DBG("state %s (%d) -> %s (%d)", + wifi_state_str(wifi_mgmt->state), wifi_mgmt->state, + wifi_state_str(state), state); + + wifi_mgmt->state = state; +} + +static void net_mgmt_event_handle(struct k_work *work) +{ + struct wifi_manager_data *wifi_mgmt = + CONTAINER_OF(work, struct wifi_manager_data, net_mgmt_event_work); + atomic_val_t net_state = atomic_get(&wifi_mgmt->net_state); - /* verify connect status */ - if (wifi.connect_status == 0) { - LOG_INF("Successfully connected to WiFi"); - break; + if (wifi_mgmt->net_state_old == net_state) { + /* Nothing to do */ + return; + } + + if (net_state & NET_STATE_IPV4_READY) { + wifi_event_notify(wifi_mgmt, WIFI_EVENT_IP_ADD); + } else if (net_state & NET_STATE_WIFI_CONNECTED) { + if (wifi_mgmt->net_state_old & NET_STATE_WIFI_CONNECTED) { + wifi_event_notify(wifi_mgmt, WIFI_EVENT_IP_DEL); } else { - LOG_ERR("Failed to connect to WiFi: %d", - wifi.connect_status); - goto retry; + wifi_event_notify(wifi_mgmt, WIFI_EVENT_CONNECTED); } + } else { + wifi_event_notify(wifi_mgmt, WIFI_EVENT_DISCONNECTED); + } + + wifi_mgmt->net_state_old = net_state; +} + +static void wifi_event_handle(struct k_work *work) +{ + struct wifi_manager_data *wifi_mgmt = + CONTAINER_OF(work, struct wifi_manager_data, event_work); + enum wifi_event event = wifi_mgmt->event; + enum wifi_state old_state = wifi_mgmt->state; + int map_value = wifi_state_change_map[event][old_state]; + enum wifi_state new_state = map_value - MAP_OFFSET; + int sleep_msec = 0; + + if (map_value - MAP_OFFSET >= WIFI_STATE_WAIT) { + new_state = old_state; + sleep_msec = map_value - MAP_OFFSET - (int) WIFI_STATE_WAIT; + } - retry: - k_sleep(K_MSEC(timeout_msec)); + if (map_value == 0) { + LOG_DBG("Nothing to do according to state map (state %s[%d])", + wifi_state_str(old_state), (int) old_state); + return; + } + + LOG_DBG("event %s (%d) (%s[%d] -> %s[%d])", wifi_event_str(event), event, + wifi_state_str(old_state), (int) old_state, + wifi_state_str(new_state), (int) new_state); + + if (new_state == WIFI_STATE_INVALID) { + LOG_ERR("Invalid event %s (%d) during state %s (%d)", + wifi_event_str(event), event, + wifi_state_str(old_state), old_state); + return; + } + + if (new_state == WIFI_STATE_INVALID) { + return; + } + + wifi_state_change(wifi_mgmt, new_state); + + if (new_state == old_state) { + /* Give a bit of time between retries */ + LOG_DBG("Sleeping for %d ms", sleep_msec); + k_sleep(K_MSEC(sleep_msec)); + } - /* Exponential backoff, but max 30s */ - timeout_msec = MIN(timeout_msec * 2, - 30 * 1000); + switch (new_state) { + case WIFI_STATE_IDLE: + break; + case WIFI_STATE_WAITING_FOR_LOWER_UP: + wifi_check_lower_up(wifi_mgmt); + break; + case WIFI_STATE_CONNECTING: + wifi_connect(wifi_mgmt); + break; + case WIFI_STATE_DISCONNECTING: + wifi_disconnect(wifi_mgmt); + break; + case WIFI_STATE_READY: + wifi_ready(wifi_mgmt); + break; + case WIFI_STATE_INVALID: + case WIFI_STATE_WAIT: + break; } +} + +static inline atomic_val_t atomic_update(atomic_t *target, + atomic_val_t value, + atomic_val_t mask) +{ + atomic_val_t flags; + + do { + flags = atomic_get(target); + } while (!atomic_cas(target, flags, (flags & ~mask) | value)); - net_mgmt_del_event_callback(&wifi.wifi_mgmt_cb); + return flags; +} + +static void ipv4_changed(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, struct net_if *iface) +{ + struct wifi_manager_data *wifi_mgmt = + CONTAINER_OF(cb, struct wifi_manager_data, ipv4_mgmt_cb); + + LOG_DBG("ipv4 event: %x", (unsigned int) mgmt_event); + + switch (mgmt_event) { + case NET_EVENT_IPV4_ADDR_ADD: + atomic_or(&wifi_mgmt->net_state, NET_STATE_IPV4_READY); + k_work_submit_to_queue(&wifi_mgmt->workq, &wifi_mgmt->net_mgmt_event_work); + break; + case NET_EVENT_IPV4_ADDR_DEL: + atomic_and(&wifi_mgmt->net_state, ~NET_STATE_IPV4_READY); + k_work_submit_to_queue(&wifi_mgmt->workq, &wifi_mgmt->net_mgmt_event_work); + break; + } } + +static inline int wifi_connect_error(const struct wifi_status *status) +{ + return status->status; +} + +static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, struct net_if *iface) +{ + struct wifi_manager_data *wifi_mgmt = + CONTAINER_OF(cb, struct wifi_manager_data, wifi_mgmt_cb); + const struct wifi_status *status = cb->info; + + LOG_DBG("wifi event: %x", (unsigned int) mgmt_event); + + switch (mgmt_event) { + case NET_EVENT_WIFI_CONNECT_RESULT: + if (wifi_connect_error(status)) { + atomic_and(&wifi_mgmt->net_state, + ~(NET_STATE_WIFI_CONNECTED | + NET_STATE_WIFI_CONNECTING | + NET_STATE_IPV4_READY)); + } else { + atomic_update(&wifi_mgmt->net_state, + NET_STATE_WIFI_CONNECTED, + (NET_STATE_WIFI_CONNECTED | + NET_STATE_WIFI_CONNECTING)); + } + k_work_submit_to_queue(&wifi_mgmt->workq, &wifi_mgmt->net_mgmt_event_work); + break; + case NET_EVENT_WIFI_DISCONNECT_RESULT: + atomic_and(&wifi_mgmt->net_state, + ~(NET_STATE_WIFI_CONNECTED | + NET_STATE_WIFI_CONNECTING | + NET_STATE_IPV4_READY)); + k_work_submit_to_queue(&wifi_mgmt->workq, &wifi_mgmt->net_mgmt_event_work); + break; + default: + break; + } +} + +static struct wifi_manager_data wifi_manager_data; + +K_THREAD_STACK_DEFINE(wifi_manager_work_q_stack, + CONFIG_GOLIOTH_SAMPLE_WIFI_STACK_SIZE); + +static const struct wifi_manager_config wifi_manager_config = { + .workq_stack = wifi_manager_work_q_stack, + .workq_stack_size = + K_THREAD_STACK_SIZEOF(wifi_manager_work_q_stack), + .workq_config = { + .name = "wifi_manager", + }, +}; + +static int wifi_manager_init(void) +{ + struct wifi_manager_data *wifi_mgmt = &wifi_manager_data; + const struct wifi_manager_config *config = &wifi_manager_config; + + wifi_mgmt->iface = net_if_get_default(); + __ASSERT_NO_MSG(wifi_mgmt->iface); + + k_work_queue_start(&wifi_mgmt->workq, + config->workq_stack, + config->workq_stack_size, + CONFIG_GOLIOTH_SAMPLE_WIFI_THREAD_PRIORITY, + &config->workq_config); + k_work_init(&wifi_mgmt->event_work, wifi_event_handle); + k_work_init(&wifi_mgmt->net_mgmt_event_work, net_mgmt_event_handle); + + net_mgmt_init_event_callback(&wifi_mgmt->ipv4_mgmt_cb, ipv4_changed, + NET_EVENT_IPV4_ADDR_ADD | + NET_EVENT_IPV4_ADDR_DEL); + net_mgmt_add_event_callback(&wifi_mgmt->ipv4_mgmt_cb); + + net_mgmt_init_event_callback(&wifi_mgmt->wifi_mgmt_cb, + wifi_mgmt_event_handler, + WIFI_MANAGER_MGMT_EVENTS); + net_mgmt_add_event_callback(&wifi_mgmt->wifi_mgmt_cb); + + wifi_event_notify(wifi_mgmt, WIFI_EVENT_START); + k_work_submit_to_queue(&wifi_mgmt->workq, &wifi_mgmt->event_work); + + return 0; +} + +SYS_INIT(wifi_manager_init, APPLICATION, CONFIG_GOLIOTH_SAMPLE_WIFI_INIT_PRIORITY);