diff --git a/doc/nrf/libraries/networking/wifi_ready.rst b/doc/nrf/libraries/networking/wifi_ready.rst new file mode 100644 index 000000000000..9e7a595f9b96 --- /dev/null +++ b/doc/nrf/libraries/networking/wifi_ready.rst @@ -0,0 +1,30 @@ +.. _lib_wifi_ready: + +Wi-Fi ready +########### + +.. contents:: + :local: + :depth: 2 + +The Wi-Fi ready library manages Wi-FiĀ® readiness for applications by handling supplicant ready and not ready events. + +Overview +******** + +The Wi-Fi ready library informs applications of Wi-Fi readiness by managing supplicant events, indicating when Wi-Fi is available for use. + +Configuration +************* + +To use this library, enable the :kconfig:option:`CONFIG_WIFI_READY_LIB` Kconfig option. + +API documentation +***************** + +| Header file: :file:`include/net/wifi_ready.h` +| Source files: :file:`subsys/net/lib/wifi_ready` + +.. doxygengroup:: wifi_ready + :project: nrf + :members: diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 7c24143e800e..2be4b67b22b6 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -295,7 +295,7 @@ This section provides detailed lists of changes by :ref:`driver `. Wi-Fi drivers ------------- -|no_changes_yet_note| +* Added a new feature to recover nRF70 chip. Libraries ========= @@ -335,7 +335,7 @@ Modem libraries Libraries for networking ------------------------ -|no_changes_yet_note| +* Added the :ref:`lib_wifi_ready` library. Libraries for NFC ----------------- diff --git a/drivers/wifi/nrf700x/Kconfig b/drivers/wifi/nrf700x/Kconfig index 524f4d0e2c28..87822b5abd7c 100644 --- a/drivers/wifi/nrf700x/Kconfig +++ b/drivers/wifi/nrf700x/Kconfig @@ -620,3 +620,130 @@ config NRF_WIFI_AP_DEAD_DETECT_TIMEOUT help The number of seconds after which AP is declared dead if no beacons are received from the AP. Used to detect AP silently going down e.g., power off. + +choice NRF_WIFI_PS_DATA_RETRIEVAL_MECHANISM + prompt "Power save data retrieval mechanism" + default NRF_WIFI_PS_POLL_BASED_RETRIEVAL + help + Select the mechanism to retrieve buffered data from AP. + +config NRF_WIFI_PS_POLL_BASED_RETRIEVAL + bool "PS-Poll frame based mechanism to retrieve buffered data from AP" + help + When AP notifies about availability of buffered data, the STA stays in power save + and retrieves the frames one-by-one, this conserved more power but adds latency + to the traffic. Ideal for minimum number of frames. + +config NRF_WIFI_QOS_NULL_BASED_RETRIEVAL + bool "QoS null frame based mechanism to retrieve buffered data from AP" + help + When AP notifies about availability of buffered data, the STA comes out of + power save and then AP can deliver all buffered frames without any additional + overhead or latency, but STA enters power save after a delay costing more power + depending on the delay. Ideal for heavy buffered traffic. + +endchoice + +config NRF_WIFI_MGMT_BUFF_OFFLOAD + bool "Enable management buffer offload" + default y + +config NRF_WIFI_RPU_RECOVERY + bool "Enable RPU recovery mechanism" + depends on NRF_WIFI_LOW_POWER + select EXPERIMENTAL + default y + help + Enable RPU recovery mechanism to recover from RPU (nRF70) hang. + This feature performs an interface reset (down and up) which triggers + a RPU coldboot. Application's network connection will be lost during + the recovery process and it is application's responsibility to + re-establish the network connection. + +config NRF_WIFI_CMD_EVENT_LOG + bool "Enable command event logging" + help + Enable command event logging to help debug Wi-Fi command events. +if NRF_WIFI_RPU_RECOVERY + +config NRF_WIFI_RPU_RECOVERY_PROPAGATION_DELAY_MS + int "RPU recovery propagation delay in milliseconds" + default 2000 + help + Propagation delay in milliseconds to wait after RPU is powered down + before powering it up. This delay is required to ensure that the recovery + is propagted to all the applications and stack and have enough time to + clean up the resources. + +config NRF_WIFI_RPU_RECOVERY_PS_ACTIVE_TIMEOUT_MS + int "RPU recovery power save active timeout in milliseconds" + default 50000 + help + Power save active timeout in milliseconds after which RPU recovery + mechanism will be triggered. This timeout is used to ensure that the + RPU attempts to enter power save mode in case of inactivity. + +config NRF_WIFI_RPU_MIN_TIME_TO_ENTER_SLEEP_MS + int "Minimum idle time to enter sleep in milliseconds" + range 100 5000 + default 1000 + help + Minimum time the host should de-assert WAKEUP_NOW and let RPU enter + sleep mode, assuming there is no activity. + +config NRF_WIFI_RPU_RECOVERY_DEBUG + bool "Enable RPU recovery debug logs" + help + Enable RPU recovery debug logs to help debug RPU recovery mechanism. + +config NRF_WIFI_RPU_RECOVERY_QUIET_PERIOD_MS + int "RPU recovery quiet period in milliseconds" + default 5000 + help + Quiet period in milliseconds after RPU recovery is triggered. During + this period, no new RPU recovery will be triggered. + +config NRF_WIFI_RPU_RECOVERY_MAX_RETRIES + int "Maximum number of consecutive RPU recovery retries, 0 to disable" + default 0 + help + Maximum number of consecutive RPU recovery retries before giving up + and resetting the system. Set to 0 to keep retrying indefinitely. + +config NRF_WIFI_RPU_RECOVERY_RETRY_WINDOW_S + int "RPU recovery retry window in seconds" + default 900 + help + Window in seconds during which the number of consecutive RPU recovery + retries are counted. If the number of consecutive RPU recovery retries + exceeds NRF_WIFI_RPU_RECOVERY_MAX_RETRIES within this window, the system + will be reset. + +config NRF_WIFI_RPU_RECOVERY_PS_STATE_DEBUG + bool "Enable RPU recovery power save state debug logs" + help + Enable RPU recovery power save state debug logs to help debug RPU recovery mechanism. + + +config NET_MGMT_EVENT_QUEUE_SIZE + # Doing interface down and up even with delay puts a lot of events in the queue + default 16 +endif + +config NRF_WIFI_FEAT_KEEPALIVE + bool "Enable Wi-Fi keepalive feature" + depends on NRF700X_STA_MODE + help + Enable Wi-Fi keepalive feature to keep the connection alive by sending + keepalive packets to the AP. Primarily intended to interoperate with APs + that disconnect idle clients without any explicit checks. Slightly increases + power consumption. + +if NRF_WIFI_FEAT_KEEPALIVE +config NRF_WIFI_KEEPALIVE_PERIOD_S + int "Keepalive period in seconds" + range 30 3600 + default 60 + help + Keepalive period in seconds to send keepalive packets to the AP. +endif diff --git a/drivers/wifi/nrf700x/inc/fmac_main.h b/drivers/wifi/nrf700x/inc/fmac_main.h index 6e2a2210e940..53b5f713fc06 100644 --- a/drivers/wifi/nrf700x/inc/fmac_main.h +++ b/drivers/wifi/nrf700x/inc/fmac_main.h @@ -69,11 +69,6 @@ struct nrf_wifi_vif_ctx_zep { struct wifi_ps_config *ps_info; bool ps_config_info_evnt; bool authorized; - struct nrf_wifi_ext_capa { - enum nrf_wifi_iftype iftype; - unsigned char *ext_capa, *ext_capa_mask; - unsigned int ext_capa_len; - } iface_ext_capa; bool cookie_resp_received; #ifdef CONFIG_NRF700X_DATA_TX struct k_work nrf_wifi_net_iface_work; @@ -81,6 +76,9 @@ struct nrf_wifi_vif_ctx_zep { unsigned long rssi_record_timestamp_us; signed short rssi; #endif /* CONFIG_NRF700X_STA_MODE */ +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY + struct k_work nrf_wifi_rpu_recovery_work; +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ }; struct nrf_wifi_vif_ctx_map { @@ -104,6 +102,12 @@ struct nrf_wifi_ctx_zep { #endif /* CONFIG_NRF700X_RADIO_TEST */ unsigned char *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; + struct k_mutex rpu_lock; +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY + bool rpu_recovery_in_progress; + unsigned long last_rpu_recovery_time_ms; + unsigned int rpu_recovery_retries; +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ }; struct nrf_wifi_drv_priv_zep { @@ -123,5 +127,10 @@ enum nrf_wifi_status nrf_wifi_fmac_dev_add_zep(struct nrf_wifi_drv_priv_zep *drv enum nrf_wifi_status nrf_wifi_fmac_dev_rem_zep(struct nrf_wifi_drv_priv_zep *drv_priv_zep); enum nrf_wifi_status nrf_wifi_fw_load(void *rpu_ctx); struct nrf_wifi_vif_ctx_zep *nrf_wifi_get_vif_ctx(struct net_if *iface); +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY +void nrf_wifi_rpu_recovery_cb(void *vif_ctx, + void *event_data, + unsigned int event_len); +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ #endif /* __ZEPHYR_FMAC_MAIN_H__ */ diff --git a/drivers/wifi/nrf700x/src/fmac_main.c b/drivers/wifi/nrf700x/src/fmac_main.c index 6a2a6d0bfa02..0630344975d7 100644 --- a/drivers/wifi/nrf700x/src/fmac_main.c +++ b/drivers/wifi/nrf700x/src/fmac_main.c @@ -249,8 +249,8 @@ static void nrf_wifi_process_rssi_from_rx(void *vif_ctx, rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; - if (!rpu_ctx_zep) { - LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { + LOG_ERR("%s: rpu_ctx_zep is NULL\n", __func__); return; } @@ -306,6 +306,7 @@ int nrf_wifi_reg_domain(const struct device *dev, struct wifi_reg_domain *reg_do { enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; struct nrf_wifi_fmac_reg_info reg_domain_info = {0}; struct wifi_reg_chan_info *chan_info = NULL; @@ -333,12 +334,18 @@ int nrf_wifi_reg_domain(const struct device *dev, struct wifi_reg_domain *reg_do goto err; } + fmac_dev_ctx = rpu_ctx_zep->rpu_ctx; + if (!fmac_dev_ctx) { + LOG_ERR("%s: fmac_dev_ctx is NULL", __func__); + goto err; + } + if (reg_domain->oper == WIFI_MGMT_SET) { memcpy(reg_domain_info.alpha2, reg_domain->country_code, WIFI_COUNTRY_CODE_LEN); reg_domain_info.force = reg_domain->force; - status = nrf_wifi_fmac_set_reg(rpu_ctx_zep->rpu_ctx, ®_domain_info); + status = nrf_wifi_fmac_set_reg(fmac_dev_ctx, ®_domain_info); if (status != NRF_WIFI_STATUS_SUCCESS) { LOG_ERR("%s: Failed to set regulatory domain", __func__); goto err; @@ -350,9 +357,9 @@ int nrf_wifi_reg_domain(const struct device *dev, struct wifi_reg_domain *reg_do goto err; } - status = nrf_wifi_fmac_get_reg(rpu_ctx_zep->rpu_ctx, ®_domain_info); + status = nrf_wifi_fmac_get_reg(fmac_dev_ctx, ®_domain_info); if (status != NRF_WIFI_STATUS_SUCCESS) { - LOG_ERR("%s: Failed to get regulatory domain", __func__); + LOG_ERR("%s: Failed to get regulatory domain\n", __func__); goto err; } @@ -625,6 +632,8 @@ enum nrf_wifi_status nrf_wifi_fmac_dev_add_zep(struct nrf_wifi_drv_priv_zep *drv goto err; } + k_mutex_init(&rpu_ctx_zep->rpu_lock); + return status; err: if (rpu_ctx) { @@ -651,6 +660,11 @@ enum nrf_wifi_status nrf_wifi_fmac_dev_rem_zep(struct nrf_wifi_drv_priv_zep *drv nrf_wifi_fmac_dev_rem(rpu_ctx_zep->rpu_ctx); #endif /* CONFIG_NRF700X_RADIO_TEST */ + free(rpu_ctx_zep->extended_capa); + rpu_ctx_zep->extended_capa = NULL; + free(rpu_ctx_zep->extended_capa_mask); + rpu_ctx_zep->extended_capa_mask = NULL; + rpu_ctx_zep->rpu_ctx = NULL; LOG_DBG("%s: FMAC device removed", __func__); @@ -689,6 +703,9 @@ static int nrf_wifi_drv_main_zep(const struct device *dev) rx_buf_pools[1].buf_sz = rx2_buf_sz; rx_buf_pools[2].buf_sz = rx3_buf_sz; +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY + callbk_fns.rpu_recovery_callbk_fn = nrf_wifi_rpu_recovery_cb; +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ callbk_fns.scan_start_callbk_fn = nrf_wifi_event_proc_scan_start_zep; callbk_fns.scan_done_callbk_fn = nrf_wifi_event_proc_scan_done_zep; callbk_fns.reg_change_callbk_fn = reg_change_callbk_fn; diff --git a/drivers/wifi/nrf700x/src/net_if.c b/drivers/wifi/nrf700x/src/net_if.c index 1261e837f294..44a463eb941b 100644 --- a/drivers/wifi/nrf700x/src/net_if.c +++ b/drivers/wifi/nrf700x/src/net_if.c @@ -14,6 +14,8 @@ #include LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF700X_LOG_LEVEL); +#include + #include "net_private.h" #include "util.h" @@ -41,25 +43,155 @@ void nrf_wifi_set_iface_event_handler(void *os_vif_ctx, if (!os_vif_ctx) { LOG_ERR("%s: Invalid parameters", __func__); - goto out; + return; } if (!event) { LOG_ERR("%s: event is NULL", __func__); - goto out; + return; } + (void)event_len; vif_ctx_zep = os_vif_ctx; vif_ctx_zep->set_if_event_received = true; vif_ctx_zep->set_if_status = event->return_value; +} + +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY +static void nrf_wifi_rpu_recovery_work_handler(struct k_work *work) +{ + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = CONTAINER_OF(work, + struct nrf_wifi_vif_ctx_zep, + nrf_wifi_rpu_recovery_work); + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + int ret; + + if (!vif_ctx_zep) { + LOG_ERR("%s: vif_ctx_zep is NULL", __func__); + return; + } + + if (!vif_ctx_zep->zep_net_if_ctx) { + LOG_ERR("%s: zep_net_if_ctx is NULL", __func__); + return; + } + + rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; + if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { + LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); + return; + } + + if (rpu_ctx_zep->rpu_recovery_in_progress) { +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY_DEBUG + LOG_ERR("%s: RPU recovery already in progress", __func__); +#else + LOG_DBG("%s: RPU recovery already in progress", __func__); +#endif + return; + } + + if (rpu_ctx_zep->last_rpu_recovery_time_ms && + ((k_uptime_get() - rpu_ctx_zep->last_rpu_recovery_time_ms) < + CONFIG_NRF_WIFI_RPU_RECOVERY_QUIET_PERIOD_MS)) { +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY_DEBUG + LOG_ERR("%s: In quiet period (last_rpu_recovery_time_ms=%ld), ignoring", + __func__, rpu_ctx_zep->last_rpu_recovery_time_ms); +#else + LOG_DBG("%s: In quiet period (last_rpu_recovery_time_ms=%ld), ignoring", + __func__, rpu_ctx_zep->last_rpu_recovery_time_ms); +#endif + return; + } +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY_DEBUG + LOG_ERR("%s: Starting RPU recovery", __func__); +#else + LOG_DBG("%s: Starting RPU recovery", __func__); +#endif + k_mutex_lock(&rpu_ctx_zep->rpu_lock, K_FOREVER); +#if CONFIG_NRF_WIFI_RPU_RECOVERY_MAX_RETRIES > 0 + if (!rpu_ctx_zep->last_rpu_recovery_time_ms || + (k_uptime_get() - rpu_ctx_zep->last_rpu_recovery_time_ms) < + CONFIG_NRF_WIFI_RPU_RECOVERY_RETRY_WINDOW_S * MSEC_PER_SEC) { + if (rpu_ctx_zep->rpu_recovery_retries >= + CONFIG_NRF_WIFI_RPU_RECOVERY_MAX_RETRIES) { + LOG_ERR("%s: Maximum recovery retries reached, rebooting system", + __func__); + sys_reboot(SYS_REBOOT_COLD); + } + rpu_ctx_zep->rpu_recovery_retries++; + } else { + rpu_ctx_zep->rpu_recovery_retries = 0; + } +#endif + rpu_ctx_zep->rpu_recovery_in_progress = true; +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY_DEBUG + LOG_ERR("%s: Bringing the interface down", __func__); +#else + LOG_DBG("%s: Bringing the interface down", __func__); +#endif + /* This indirectly does a cold-boot of RPU */ + ret = net_if_down(vif_ctx_zep->zep_net_if_ctx); + if (ret) { + LOG_ERR("%s: net_if_down failed: %d", __func__, ret); + /* Continue with the recovery */ + } + k_msleep(CONFIG_NRF_WIFI_RPU_RECOVERY_PROPAGATION_DELAY_MS); +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY_DEBUG + LOG_ERR("%s: Bringing the interface up", __func__); +#else + LOG_DBG("%s: Bringing the interface up", __func__); +#endif + ret = net_if_up(vif_ctx_zep->zep_net_if_ctx); + if (ret) { + LOG_ERR("%s: net_if_up failed: %d", __func__, ret); + } + rpu_ctx_zep->rpu_recovery_in_progress = false; + rpu_ctx_zep->last_rpu_recovery_time_ms = k_uptime_get(); + k_mutex_unlock(&rpu_ctx_zep->rpu_lock); +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY_DEBUG + LOG_ERR("%s: RPU recovery done", __func__); +#else + LOG_DBG("%s: RPU recovery done", __func__); +#endif +} + +void nrf_wifi_rpu_recovery_cb(void *vif_ctx_handle, + void *event_data, + unsigned int event_len) +{ + struct nrf_wifi_fmac_vif_ctx *vif_ctx = vif_ctx_handle; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL; + struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; + + if (!vif_ctx) { + LOG_ERR("%s: vif_ctx is NULL", + __func__); + goto out; + } + + fmac_dev_ctx = vif_ctx->fmac_dev_ctx; + def_dev_ctx = wifi_dev_priv(fmac_dev_ctx); + if (!def_dev_ctx) { + LOG_ERR("%s: def_dev_ctx is NULL", + __func__); + goto out; + } + + vif_ctx_zep = vif_ctx->os_vif_ctx; + (void)event_data; + + k_work_submit(&vif_ctx_zep->nrf_wifi_rpu_recovery_work); out: return; } +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ #ifdef CONFIG_NRF700X_DATA_TX static void nrf_wifi_net_iface_work_handler(struct k_work *work) @@ -510,6 +642,11 @@ void nrf_wifi_if_init_zep(struct net_if *iface) nrf_wifi_net_iface_work_handler); #endif /* CONFIG_NRF700X_DATA_TX */ +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY + k_work_init(&vif_ctx_zep->nrf_wifi_rpu_recovery_work, + nrf_wifi_rpu_recovery_work_handler); +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ + #if !defined(CONFIG_NRF_WIFI_IF_AUTO_START) net_if_flag_set(iface, NET_IF_NO_AUTO_START); #endif /* CONFIG_NRF_WIFI_IF_AUTO_START */ @@ -710,14 +847,14 @@ int nrf_wifi_if_stop_zep(const struct device *dev) ret = k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER); if (ret != 0) { LOG_ERR("%s: Failed to lock vif_lock", __func__); - goto out; + goto unlock; } rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; if (!rpu_ctx_zep) { LOG_ERR("%s: rpu_ctx_zep is NULL", __func__); - goto out; + goto unlock; } #ifdef CONFIG_NRF700X_STA_MODE @@ -729,7 +866,6 @@ int nrf_wifi_if_stop_zep(const struct device *dev) if (status != NRF_WIFI_STATUS_SUCCESS) { LOG_ERR("%s: nrf_wifi_fmac_set_power_save failed", __func__); - goto unlock; } #endif /* CONFIG_NRF_WIFI_LOW_POWER */ #endif /* CONFIG_NRF700X_STA_MODE */ @@ -748,7 +884,6 @@ int nrf_wifi_if_stop_zep(const struct device *dev) if (status != NRF_WIFI_STATUS_SUCCESS) { LOG_ERR("%s: nrf_wifi_fmac_chg_vif_state failed", __func__); - goto unlock; } status = nrf_wifi_fmac_del_vif(rpu_ctx_zep->rpu_ctx, @@ -757,11 +892,8 @@ int nrf_wifi_if_stop_zep(const struct device *dev) if (status != NRF_WIFI_STATUS_SUCCESS) { LOG_ERR("%s: nrf_wifi_fmac_del_vif failed", __func__); - goto unlock; } - vif_ctx_zep->if_op_state = NRF_WIFI_FMAC_IF_OP_STATE_DOWN; - if (nrf_wifi_fmac_get_num_vifs(rpu_ctx_zep->rpu_ctx) == 0) { nrf_wifi_fmac_dev_rem_zep(&rpu_drv_priv_zep); } diff --git a/drivers/wifi/nrf700x/src/qspi/inc/spi_if.h b/drivers/wifi/nrf700x/src/qspi/inc/spi_if.h index f5cce2e50d8b..93ae191d7ea5 100644 --- a/drivers/wifi/nrf700x/src/qspi/inc/spi_if.h +++ b/drivers/wifi/nrf700x/src/qspi/inc/spi_if.h @@ -13,6 +13,8 @@ int spim_init(struct qspi_config *config); +int spim_deinit(void); + int spim_write(unsigned int addr, const void *data, int len); int spim_read(unsigned int addr, void *data, int len); diff --git a/drivers/wifi/nrf700x/src/qspi/src/device.c b/drivers/wifi/nrf700x/src/qspi/src/device.c index c68c1ee89e63..86cd724b0b9e 100644 --- a/drivers/wifi/nrf700x/src/qspi/src/device.c +++ b/drivers/wifi/nrf700x/src/qspi/src/device.c @@ -21,15 +21,21 @@ static struct qspi_config config; #if defined(CONFIG_NRF700X_ON_QSPI) -static struct qspi_dev qspi = { .init = qspi_init, - .read = qspi_read, - .write = qspi_write, - .hl_read = qspi_hl_read}; +static struct qspi_dev qspi = { + .init = qspi_init, + .deinit = qspi_deinit, + .read = qspi_read, + .write = qspi_write, + .hl_read = qspi_hl_read +}; #else -static struct qspi_dev spim = { .init = spim_init, - .read = spim_read, - .write = spim_write, - .hl_read = spim_hl_read}; +static struct qspi_dev spim = { + .init = spim_init, + .deinit = spim_deinit, + .read = spim_read, + .write = spim_write, + .hl_read = spim_hl_read +}; #endif struct qspi_config *qspi_defconfig(void) diff --git a/drivers/wifi/nrf700x/src/qspi/src/rpu_hw_if.c b/drivers/wifi/nrf700x/src/qspi/src/rpu_hw_if.c index 2b9ec91027c7..f65a05e9b0cd 100644 --- a/drivers/wifi/nrf700x/src/qspi/src/rpu_hw_if.c +++ b/drivers/wifi/nrf700x/src/qspi/src/rpu_hw_if.c @@ -462,6 +462,31 @@ int rpu_clks_on(void) return 0; } +#define RPU_EXP_SIG 0x42000020 +/* Read a known value from RPU memory to validate RPU communication */ +int rpu_validate_comms(void) +{ + uint32_t rpu_test; + int ret; + + /* UCCP_SOC_FAB_MST_READ_IDLE - HW reset value */ + ret = rpu_read(0x0005C, &rpu_test, 4); + if (ret) { + LOG_ERR("Error: RPU comms test: read failed\n"); + return ret; + } + + if (rpu_test != RPU_EXP_SIG) { + LOG_ERR("Error: RPU comms test: sig failed: expected 0x%x, got 0x%x\n", + RPU_EXP_SIG, rpu_test); + return -1; + } + + LOG_DBG("RPU comms test passed\n"); + + return 0; +} + #define CALL_RPU_FUNC(func, ...) \ do { \ ret = func(__VA_ARGS__); \ @@ -514,6 +539,11 @@ int rpu_enable(void) goto rpu_pwroff; } + ret = rpu_validate_comms(); + if (ret) { + goto rpu_pwroff; + } + return 0; rpu_pwroff: rpu_pwroff(); diff --git a/drivers/wifi/nrf700x/src/qspi/src/spi_if.c b/drivers/wifi/nrf700x/src/qspi/src/spi_if.c index 3c569fc57f42..51180ad03287 100644 --- a/drivers/wifi/nrf700x/src/qspi/src/spi_if.c +++ b/drivers/wifi/nrf700x/src/qspi/src/spi_if.c @@ -243,6 +243,13 @@ int spim_init(struct qspi_config *config) return 0; } +int spim_deinit(void) +{ + LOG_DBG("TODO : %s", __func__); + + return 0; +} + static void spim_addr_check(unsigned int addr, const void *data, unsigned int len) { if ((addr % 4 != 0) || (((unsigned int)data) % 4 != 0) || (len % 4 != 0)) { diff --git a/drivers/wifi/nrf700x/src/shim.c b/drivers/wifi/nrf700x/src/shim.c index 42bc6bc005f0..e8d05804252f 100644 --- a/drivers/wifi/nrf700x/src/shim.c +++ b/drivers/wifi/nrf700x/src/shim.c @@ -238,9 +238,8 @@ static void *zep_shim_nbuf_alloc(unsigned int size) static void zep_shim_nbuf_free(void *nbuf) { - struct nwb *nwb; - - nwb = nbuf; + if (!nbuf) + return; k_free(((struct nwb *)nbuf)->priv); @@ -626,6 +625,20 @@ static unsigned int zep_shim_time_elapsed_us(unsigned long start_time_us) return curr_time_us - start_time_us; } +static unsigned long zep_shim_time_get_curr_ms(void) +{ + return k_uptime_get(); +} + +static unsigned int zep_shim_time_elapsed_ms(unsigned long start_time_ms) +{ + unsigned long curr_time_ms = 0; + + curr_time_ms = zep_shim_time_get_curr_ms(); + + return curr_time_ms - start_time_ms; +} + static enum nrf_wifi_status zep_shim_bus_qspi_dev_init(void *os_qspi_dev_ctx) { ARG_UNUSED(os_qspi_dev_ctx); @@ -633,11 +646,10 @@ static enum nrf_wifi_status zep_shim_bus_qspi_dev_init(void *os_qspi_dev_ctx) return NRF_WIFI_STATUS_SUCCESS; } -static void zep_shim_bus_qspi_dev_deinit(void *os_qspi_dev_ctx) +static void zep_shim_bus_qspi_dev_deinit(void *priv) { - struct qspi_dev *dev = NULL; - - dev = os_qspi_dev_ctx; + struct zep_shim_bus_qspi_priv *qspi_priv = priv; + struct qspi_dev *dev = qspi_priv->qspi_dev; dev->deinit(); } @@ -645,7 +657,7 @@ static void zep_shim_bus_qspi_dev_deinit(void *os_qspi_dev_ctx) static void *zep_shim_bus_qspi_dev_add(void *os_qspi_priv, void *osal_qspi_dev_ctx) { struct zep_shim_bus_qspi_priv *zep_qspi_priv = os_qspi_priv; - struct qspi_dev *qdev = qspi_dev(); + struct qspi_dev *dev = qspi_dev(); int ret; enum nrf_wifi_status status; @@ -655,7 +667,7 @@ static void *zep_shim_bus_qspi_dev_add(void *os_qspi_priv, void *osal_qspi_dev_c return NULL; } - status = qdev->init(qspi_defconfig()); + status = dev->init(qspi_defconfig()); if (status != NRF_WIFI_STATUS_SUCCESS) { LOG_ERR("%s: QSPI device init failed", __func__); return NULL; @@ -666,17 +678,18 @@ static void *zep_shim_bus_qspi_dev_add(void *os_qspi_priv, void *osal_qspi_dev_c LOG_ERR("%s: RPU enable failed with error %d", __func__, ret); return NULL; } - zep_qspi_priv->qspi_dev = qdev; + zep_qspi_priv->qspi_dev = dev; zep_qspi_priv->dev_added = true; return zep_qspi_priv; } -static void zep_shim_bus_qspi_dev_rem(void *os_qspi_dev_ctx) +static void zep_shim_bus_qspi_dev_rem(void *priv) { - struct qspi_dev *dev = NULL; + struct zep_shim_bus_qspi_priv *qspi_priv = priv; + struct qspi_dev *dev = qspi_priv->qspi_dev; - dev = os_qspi_dev_ctx; + ARG_UNUSED(dev); /* TODO: Make qspi_dev a dynamic instance and remove it here */ rpu_disable(); @@ -938,6 +951,8 @@ static const struct nrf_wifi_osal_ops nrf_wifi_os_zep_ops = { .delay_us = k_usleep, .time_get_curr_us = zep_shim_time_get_curr_us, .time_elapsed_us = zep_shim_time_elapsed_us, + .time_get_curr_ms = zep_shim_time_get_curr_ms, + .time_elapsed_ms = zep_shim_time_elapsed_ms, .bus_qspi_init = zep_shim_bus_qspi_init, .bus_qspi_deinit = zep_shim_bus_qspi_deinit, diff --git a/drivers/wifi/nrf700x/src/wifi_util.c b/drivers/wifi/nrf700x/src/wifi_util.c index cee09ba60795..c7af2c5bb030 100644 --- a/drivers/wifi/nrf700x/src/wifi_util.c +++ b/drivers/wifi/nrf700x/src/wifi_util.c @@ -887,6 +887,39 @@ static int nrf_wifi_util_dump_rpu_stats(const struct shell *shell, } #endif /* CONFIG_NRF700X_RADIO_TEST */ +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY +static int nrf_wifi_util_trigger_rpu_recovery(const struct shell *shell, + size_t argc, + const char *argv[]) +{ + enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL; + struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL; + + if (!ctx || !ctx->rpu_ctx) { + shell_fprintf(shell, + SHELL_ERROR, + "RPU context not initialized\n"); + return -ENOEXEC; + } + + fmac_dev_ctx = ctx->rpu_ctx; + + status = nrf_wifi_fmac_rpu_recovery_callback(fmac_dev_ctx, NULL, 0); + if (status != NRF_WIFI_STATUS_SUCCESS) { + shell_fprintf(shell, + SHELL_ERROR, + "Failed to trigger RPU recovery\n"); + return -ENOEXEC; + } + + shell_fprintf(shell, + SHELL_INFO, + "RPU recovery triggered\n"); + + return 0; +} +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ + SHELL_STATIC_SUBCMD_SET_CREATE( nrf_wifi_util_subcmds, SHELL_CMD_ARG(he_ltf, @@ -982,6 +1015,14 @@ SHELL_STATIC_SUBCMD_SET_CREATE( 1, 1), #endif /* CONFIG_NRF700X_RADIO_TEST */ +#ifdef CONFIG_NRF_WIFI_RPU_RECOVERY + SHELL_CMD_ARG(rpu_recovery_test, + NULL, + "Trigger RPU recovery", + nrf_wifi_util_trigger_rpu_recovery, + 1, + 0), +#endif /* CONFIG_NRF_WIFI_RPU_RECOVERY */ SHELL_SUBCMD_SET_END); diff --git a/drivers/wifi/nrf700x/src/wpa_supp_if.c b/drivers/wifi/nrf700x/src/wpa_supp_if.c index 24a0e2074d5e..35537fc56c32 100644 --- a/drivers/wifi/nrf700x/src/wpa_supp_if.c +++ b/drivers/wifi/nrf700x/src/wpa_supp_if.c @@ -1550,41 +1550,6 @@ enum nrf_wifi_status nrf_wifi_parse_sband( return WLAN_STATUS_SUCCESS; } -void *nrf_wifi_memdup(const void *src, size_t len) -{ - void *r = malloc(len); - - if (r && src) { - memcpy(r, src, len); - } - return r; -} - -static void nrf_wifi_wiphy_info_extended_capab_cfg(struct nrf_wifi_ext_capa *capa, - struct nrf_wifi_event_get_wiphy *wiphy_info) -{ - unsigned int len; - - capa->iftype = NRF_WIFI_IFTYPE_STATION; - - len = wiphy_info->extended_capabilities_len; - - capa->ext_capa = nrf_wifi_memdup(&wiphy_info->extended_capabilities[0], len); - - if (!capa->ext_capa) - return; - - capa->ext_capa_len = len; - - len = wiphy_info->extended_capabilities_len; - - capa->ext_capa_mask = - nrf_wifi_memdup(&wiphy_info->extended_capabilities_mask[0], len); - - if (!capa->ext_capa_mask) - return; -} - void nrf_wifi_wpa_supp_event_get_wiphy(void *if_priv, struct nrf_wifi_event_get_wiphy *wiphy_info, unsigned int event_len) @@ -1636,8 +1601,6 @@ void nrf_wifi_wpa_supp_event_get_wiphy(void *if_priv, } } - nrf_wifi_wiphy_info_extended_capab_cfg(&vif_ctx_zep->iface_ext_capa, wiphy_info); - if (vif_ctx_zep->supp_drv_if_ctx && vif_ctx_zep->supp_callbk_fns.get_wiphy_res) { vif_ctx_zep->supp_callbk_fns.get_wiphy_res(vif_ctx_zep->supp_drv_if_ctx, NULL); } @@ -1799,10 +1762,6 @@ int nrf_wifi_supp_get_capa(void *if_priv, struct wpa_driver_capa *capa) capa->extended_capa = rpu_ctx_zep->extended_capa; capa->extended_capa_mask = rpu_ctx_zep->extended_capa_mask; capa->extended_capa_len = rpu_ctx_zep->extended_capa_len; - - capa->extended_capa = vif_ctx_zep->iface_ext_capa.ext_capa; - capa->extended_capa_mask = vif_ctx_zep->iface_ext_capa.ext_capa_mask; - capa->extended_capa_len = vif_ctx_zep->iface_ext_capa.ext_capa_len; } out: k_mutex_unlock(&vif_ctx_zep->vif_lock); diff --git a/include/net/wifi_ready.h b/include/net/wifi_ready.h new file mode 100644 index 000000000000..3db6738e118e --- /dev/null +++ b/include/net/wifi_ready.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + + +#ifndef __LIB_WIFI_READY_H__ +#define __LIB_WIFI_READY_H__ + +#include + +/** + * @defgroup wifi_ready Wi-Fi ready library + * @brief Library for handling Wi-Fi ready events. + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Structure for storing a callback function to be called when the Wi-Fi is ready. + */ +typedef struct { + /** Callback function to be called when the Wi-Fi is ready. */ + void (*wifi_ready_cb)(bool wifi_ready); + /** Interface to which the callback is registered. */ + struct net_if *iface; + /** Only for internal use. */ + struct k_work item; +} wifi_ready_callback_t; + +/** + * @brief Register a callback to be called when the Wi-Fi is ready. + * + * @param cb Callback function to be called when the Wi-Fi is ready. + * The callback is called from NET_MGMT thread, so, care should be taken + * to avoid blocking the thread and also stack size should be considered. + * @param iface (optional) Interface to which the callback is registered. + * + * @return 0 if the callback was successfully registered, or a negative error code + * if the callback could not be registered. + * + * @retval -EINVAL if the callback is NULL. + * @retval -ENOMEM if the callback array is full. + * @retval -EALREADY if the callback is already registered. + */ + +int register_wifi_ready_callback(wifi_ready_callback_t cb, struct net_if *iface); +/** + * @brief Unregister a callback that was registered to be called when the Wi-Fi is ready. + * + * @param cb Callback function to be unregistered. + * @param iface (optional) Interface to which the callback is registered. + * + * @return 0 if the callback was successfully unregistered, or a negative error code + * if the callback could not be unregistered. + * + * @retval -EINVAL if the callback is NULL. + * @retval -ENOENT if the callback is not registered. + */ +int unregister_wifi_ready_callback(wifi_ready_callback_t cb, struct net_if *iface); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __LIB_WIFI_READY_H__*/ diff --git a/samples/wifi/sta/Kconfig b/samples/wifi/sta/Kconfig index 8b7c04bba40a..f66ca4d475d1 100644 --- a/samples/wifi/sta/Kconfig +++ b/samples/wifi/sta/Kconfig @@ -63,4 +63,10 @@ config STA_CONN_TIMEOUT_SEC Specify the connection timeout, in seconds. This is the overall timeout i.e., time to be waited for a station to connect and get an IP address. DHCP retries should be taken into account when setting this value. If the timeout is set to 0, the connection will not timeout. + +config STA_SAMPLE_START_WIFI_THREAD_STACK_SIZE + int "Stack size for Wi-Fi start thread" + default 4096 + help + Set the stack size for the Wi-Fi start thread. endmenu diff --git a/samples/wifi/sta/README.rst b/samples/wifi/sta/README.rst index e1d71304b95a..ac0c04f58209 100644 --- a/samples/wifi/sta/README.rst +++ b/samples/wifi/sta/README.rst @@ -23,6 +23,9 @@ The sample can perform Wi-Fi operations such as connect and disconnect in the 2. Using this sample, the development kit can connect to the specified access point in :abbr:`STA (Station)` mode. +The sample uses the :ref:`lib_wifi_ready` library to check Wi-Fi readiness. +To use the :ref:`lib_wifi_ready` library, enable the :kconfig:option:`CONFIG_WIFI_READY_LIB` Kconfig option. + User interface ************** @@ -38,32 +41,26 @@ Configuration |config| -You must configure the following Wi-Fi credentials in the :file:`prj.conf` file: - -* Network name (SSID) -* Key management -* Password - -.. note:: - You can also use ``menuconfig`` to enable ``Key management`` option. - -See :ref:`zephyr:menuconfig` in the Zephyr documentation for instructions on how to run ``menuconfig``. - Configuration options ===================== -The following sample-specific Kconfig option is used in this sample (located in :file:`samples/wifi/sta/Kconfig`): +The following sample-specific Kconfig options are used in this sample (located in :file:`samples/wifi/sta/Kconfig`): + +.. options-from-kconfig:: + :show-type: -.. _CONFIG_NRF700X_QSPI_ENCRYPTION_KEY: +You must configure the following Wi-Fi credentials in the :file:`prj.conf` file: -CONFIG_NRF700X_QSPI_ENCRYPTION_KEY - This option specifies the QSPI encryption key. +.. note:: + You can also use ``menuconfig`` to configure ``Wi-Fi credentials``. + +See :ref:`zephyr:menuconfig` in the Zephyr documentation for instructions on how to run ``menuconfig``. Quad Serial Peripheral Interface (QSPI) encryption ************************************************** This sample demonstrates QSPI encryption API usage. -You can set the key using the :ref:`CONFIG_NRF700X_QSPI_ENCRYPTION_KEY ` Kconfig option. +You can set the key using the :kconfig:option:`CONFIG_NRF700X_QSPI_ENCRYPTION_KEY` Kconfig option. If encryption of the QSPI traffic is required for the production devices, matching keys must be programmed in both the nRF7002 OTP and non-volatile storage associated with the host. The key from non-volatile storage must be set as the encryption key using the APIs. @@ -97,12 +94,12 @@ Building and running Currently, only the nRF7002 DK is supported. -To build for the nRF7002 DK, use the ``nrf7002dk_nrf5340_cpuapp`` build target. +To build for the nRF7002 DK, use the ``nrf7002dk/nrf5340/cpuapp`` board target. The following is an example of the CLI command: .. code-block:: console - west build -b nrf7002dk_nrf5340_cpuapp + west build -b nrf7002dk/nrf5340/cpuapp Testing ======= @@ -184,6 +181,30 @@ Testing [00:00:07.720,245] sta: RSSI: -57 [00:00:07.720,245] sta: Static IP address: +RPU recovery +************ + +The RPU recovery mechanism is used to recover from the RPU (nRF70) hang. +This feature performs an interface reset (down and up), which triggers a RPU cold boot. +Application's network connection will be lost during the recovery process, and it is application's responsibility to reestablish the network connection. + +Testing +======= + +To test RPU recovery, you must build the sample with :kconfig:option:`CONFIG_SHELL` and :kconfig:option:`CONFIG_NRF700X_UTIL` Kconfig options. + +#. Trigger RPU recovery using the following command: + + .. code-block:: console + + wifi_util rpu_recovery_test + + If RPU recovery is triggered, you should see an output similar to the following: + + .. code-block:: console + + RPU recovery triggered + Power management testing ************************ @@ -199,6 +220,8 @@ See :ref:`app_power_opt` for more information on power management testing and us Dependencies ************ -This sample uses the following library: +This sample uses the following |NCS| libraries: * :ref:`nrf_security` +* :ref:`lib_wifi_ready` +* :ref:`lib_wifi_credentials` diff --git a/samples/wifi/sta/boards/nrf52840dk_nrf52840.conf b/samples/wifi/sta/boards/nrf52840dk_nrf52840.conf new file mode 100644 index 000000000000..2e3d175efa06 --- /dev/null +++ b/samples/wifi/sta/boards/nrf52840dk_nrf52840.conf @@ -0,0 +1 @@ +CONFIG_WIFI_READY_LIB=n diff --git a/samples/wifi/sta/prj.conf b/samples/wifi/sta/prj.conf index 0c9f56023f92..f4d0cd845e57 100644 --- a/samples/wifi/sta/prj.conf +++ b/samples/wifi/sta/prj.conf @@ -8,18 +8,14 @@ CONFIG_WIFI_NRF700X=y # WPA supplicant CONFIG_WPA_SUPP=y +CONFIG_WIFI_READY_LIB=y +CONFIG_NRF_WIFI_RPU_RECOVERY=y -# Below configs need to be modified based on security -# CONFIG_STA_KEY_MGMT_NONE=y -# CONFIG_STA_KEY_MGMT_WPA2=y -# CONFIG_STA_KEY_MGMT_WPA2_256=y -# CONFIG_STA_KEY_MGMT_WPA3=y -CONFIG_STA_SAMPLE_SSID="Myssid" -CONFIG_STA_SAMPLE_PASSWORD="Mypassword" - -# System settings -CONFIG_NEWLIB_LIBC=y -CONFIG_NEWLIB_LIBC_NANO=n +CONFIG_WIFI_MGMT_EXT=y +CONFIG_WIFI_CREDENTIALS=y +CONFIG_WIFI_CREDENTIALS_STATIC=y +CONFIG_WIFI_CREDENTIALS_STATIC_SSID="Myssid" +CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="Mypassword" # Networking CONFIG_NETWORKING=y @@ -35,10 +31,11 @@ CONFIG_NET_PKT_TX_COUNT=8 # Below section is the primary contributor to SRAM and is currently # tuned for performance, but this will be revisited in the future. -CONFIG_NET_BUF_RX_COUNT=16 +CONFIG_NET_BUF_RX_COUNT=8 CONFIG_NET_BUF_TX_COUNT=16 CONFIG_NET_BUF_DATA_SIZE=128 -CONFIG_HEAP_MEM_POOL_SIZE=153600 +CONFIG_NRF700X_RX_NUM_BUFS=16 +CONFIG_HEAP_MEM_POOL_SIZE=90000 CONFIG_NET_TC_TX_COUNT=1 CONFIG_NET_IF_UNICAST_IPV4_ADDR_COUNT=1 diff --git a/samples/wifi/sta/sample.yaml b/samples/wifi/sta/sample.yaml index 2c927a8a471e..97e269b424da 100644 --- a/samples/wifi/sta/sample.yaml +++ b/samples/wifi/sta/sample.yaml @@ -9,6 +9,13 @@ tests: - nrf7002dk_nrf5340_cpuapp platform_allow: nrf7002dk_nrf5340_cpuapp tags: ci_build + sample.nrf7002.sta.no_wifi_ready: + build_only: true + extra_args: CONFIG_WIFI_READY_LIB=n + integration_platforms: + - nrf7002dk_nrf5340_cpuapp + platform_allow: nrf7002dk_nrf5340_cpuapp + tags: ci_build sample.nrf7001.sta: build_only: true integration_platforms: @@ -21,8 +28,10 @@ tests: extra_args: SHIELD=nrf7002ek integration_platforms: - nrf5340dk_nrf5340_cpuapp - - nrf52840dk_nrf52840 platform_allow: nrf5340dk_nrf5340_cpuapp nrf52840dk_nrf52840 + extra_configs: + - CONFIG_NET_TX_STACK_SIZE=3700 + - CONFIG_NET_RX_STACK_SIZE=3700 tags: ci_build sample.nrf7001_eks.sta: build_only: true @@ -31,6 +40,9 @@ tests: - nrf5340dk_nrf5340_cpuapp - nrf52840dk_nrf52840 platform_allow: nrf5340dk_nrf5340_cpuapp nrf52840dk_nrf52840 + extra_configs: + - CONFIG_NET_TX_STACK_SIZE=3700 + - CONFIG_NET_RX_STACK_SIZE=3700 tags: ci_build sample.nrf7002_eb.thingy53.sta: build_only: true diff --git a/samples/wifi/sta/src/main.c b/samples/wifi/sta/src/main.c index 83ccd49843fb..5e1cc71a9d75 100644 --- a/samples/wifi/sta/src/main.c +++ b/samples/wifi/sta/src/main.c @@ -24,6 +24,9 @@ LOG_MODULE_REGISTER(sta, CONFIG_LOG_DEFAULT_LEVEL); #include #include +#include +#include + #include #include "net_private.h" @@ -50,6 +53,11 @@ static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios); static struct net_mgmt_event_callback wifi_shell_mgmt_cb; static struct net_mgmt_event_callback net_shell_mgmt_cb; +#ifdef CONFIG_WIFI_READY_LIB +static K_SEM_DEFINE(wifi_ready_state_changed_sem, 0, 1); +static bool wifi_ready_status; +#endif /* CONFIG_WIFI_READY_LIB */ + static struct { const struct shell *sh; union { @@ -207,54 +215,14 @@ static void net_mgmt_event_handler(struct net_mgmt_event_callback *cb, } } -static int __wifi_args_to_params(struct wifi_connect_req_params *params) -{ - - params->timeout = CONFIG_STA_CONN_TIMEOUT_SEC * MSEC_PER_SEC; - - if (params->timeout == 0) { - params->timeout = SYS_FOREVER_MS; - } - - /* Defaults */ - params->band = WIFI_FREQ_BAND_UNKNOWN; - params->channel = WIFI_CHANNEL_ANY; - params->security = WIFI_SECURITY_TYPE_NONE; - params->mfp = WIFI_MFP_OPTIONAL; - - /* SSID */ - params->ssid = CONFIG_STA_SAMPLE_SSID; - params->ssid_length = strlen(params->ssid); - -#if defined(CONFIG_STA_KEY_MGMT_WPA2) - params->security = 1; -#elif defined(CONFIG_STA_KEY_MGMT_WPA2_256) - params->security = 2; -#elif defined(CONFIG_STA_KEY_MGMT_WPA3) - params->security = 3; -#else - params->security = 0; -#endif - -#if !defined(CONFIG_STA_KEY_MGMT_NONE) - params->psk = CONFIG_STA_SAMPLE_PASSWORD; - params->psk_length = strlen(params->psk); -#endif - - return 0; -} - static int wifi_connect(void) { - struct net_if *iface = net_if_get_default(); - static struct wifi_connect_req_params cnx_params; + struct net_if *iface = net_if_get_first_wifi(); context.connected = false; context.connect_result = false; - __wifi_args_to_params(&cnx_params); - if (net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, - &cnx_params, sizeof(struct wifi_connect_req_params))) { + if (net_mgmt(NET_REQUEST_WIFI_CONNECT_STORED, iface, NULL, 0)) { LOG_ERR("Connection request failed"); return -ENOEXEC; @@ -285,31 +253,13 @@ int bytes_from_str(const char *str, uint8_t *bytes, size_t bytes_len) return 0; } -int main(void) +int start_app(void) { - memset(&context, 0, sizeof(context)); - - net_mgmt_init_event_callback(&wifi_shell_mgmt_cb, - wifi_mgmt_event_handler, - WIFI_SHELL_MGMT_EVENTS); - - net_mgmt_add_event_callback(&wifi_shell_mgmt_cb); - - - net_mgmt_init_event_callback(&net_shell_mgmt_cb, - net_mgmt_event_handler, - NET_EVENT_IPV4_DHCP_BOUND); - - net_mgmt_add_event_callback(&net_shell_mgmt_cb); - - LOG_INF("Starting %s with CPU frequency: %d MHz", CONFIG_BOARD, SystemCoreClock/MHZ(1)); - k_sleep(K_SECONDS(1)); - #if defined(CONFIG_BOARD_NRF7002DK_NRF7001_NRF5340_CPUAPP) || \ defined(CONFIG_BOARD_NRF7002DK_NRF5340_CPUAPP) if (strlen(CONFIG_NRF700X_QSPI_ENCRYPTION_KEY)) { - char key[QSPI_KEY_LEN_BYTES]; int ret; + char key[QSPI_KEY_LEN_BYTES]; ret = bytes_from_str(CONFIG_NRF700X_QSPI_ENCRYPTION_KEY, key, sizeof(key)); if (ret) { @@ -340,18 +290,134 @@ int main(void) CONFIG_NET_CONFIG_MY_IPV4_GW); while (1) { +#ifdef CONFIG_WIFI_READY_LIB + int ret; + + LOG_INF("Waiting for Wi-Fi to be ready"); + ret = k_sem_take(&wifi_ready_state_changed_sem, K_FOREVER); + if (ret) { + LOG_ERR("Failed to take semaphore: %d", ret); + return ret; + } + +check_wifi_ready: + if (!wifi_ready_status) { + LOG_INF("Wi-Fi is not ready"); + /* Perform any cleanup and stop using Wi-Fi and wait for + * Wi-Fi to be ready + */ + continue; + } +#endif /* CONFIG_WIFI_READY_LIB */ wifi_connect(); while (!context.connect_result) { cmd_wifi_status(); +#ifdef CONFIG_WIFI_READY_LIB + if (!wifi_ready_status) { + goto check_wifi_ready; + } +#endif /* CONFIG_WIFI_READY_LIB */ k_sleep(K_MSEC(STATUS_POLLING_MS)); } if (context.connected) { cmd_wifi_status(); +#ifdef CONFIG_WIFI_READY_LIB + ret = k_sem_take(&wifi_ready_state_changed_sem, K_FOREVER); + if (ret) { + LOG_ERR("Failed to take semaphore: %d", ret); + return ret; + } + goto check_wifi_ready; +#else k_sleep(K_FOREVER); +#endif /* CONFIG_WIFI_READY_LIB */ } } return 0; } + +#ifdef CONFIG_WIFI_READY_LIB +void start_wifi_thread(void); +#define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1) +K_THREAD_DEFINE(start_wifi_thread_id, CONFIG_STA_SAMPLE_START_WIFI_THREAD_STACK_SIZE, + start_wifi_thread, NULL, NULL, NULL, + THREAD_PRIORITY, 0, -1); + +void start_wifi_thread(void) +{ + start_app(); +} + +void wifi_ready_cb(bool wifi_ready) +{ + LOG_DBG("Is Wi-Fi ready?: %s", wifi_ready ? "yes" : "no"); + wifi_ready_status = wifi_ready; + k_sem_give(&wifi_ready_state_changed_sem); +} +#endif /* CONFIG_WIFI_READY_LIB */ + +void net_mgmt_callback_init(void) +{ + memset(&context, 0, sizeof(context)); + + net_mgmt_init_event_callback(&wifi_shell_mgmt_cb, + wifi_mgmt_event_handler, + WIFI_SHELL_MGMT_EVENTS); + + net_mgmt_add_event_callback(&wifi_shell_mgmt_cb); + + net_mgmt_init_event_callback(&net_shell_mgmt_cb, + net_mgmt_event_handler, + NET_EVENT_IPV4_DHCP_BOUND); + + net_mgmt_add_event_callback(&net_shell_mgmt_cb); + + LOG_INF("Starting %s with CPU frequency: %d MHz", CONFIG_BOARD, SystemCoreClock/MHZ(1)); + k_sleep(K_SECONDS(1)); +} + +#ifdef CONFIG_WIFI_READY_LIB +static int register_wifi_ready(void) +{ + int ret = 0; + wifi_ready_callback_t cb; + struct net_if *iface = net_if_get_first_wifi(); + + if (!iface) { + LOG_ERR("Failed to get Wi-Fi interface"); + return -1; + } + + cb.wifi_ready_cb = wifi_ready_cb; + + LOG_DBG("Registering Wi-Fi ready callbacks"); + ret = register_wifi_ready_callback(cb, iface); + if (ret) { + LOG_ERR("Failed to register Wi-Fi ready callbacks %s", strerror(ret)); + return ret; + } + + return ret; +} +#endif /* CONFIG_WIFI_READY_LIB */ + +int main(void) +{ + int ret = 0; + + net_mgmt_callback_init(); + +#ifdef CONFIG_WIFI_READY_LIB + ret = register_wifi_ready(); + if (ret) { + return ret; + } + k_thread_start(start_wifi_thread_id); +#else + start_app(); +#endif /* CONFIG_WIFI_READY_LIB */ + return ret; +} diff --git a/subsys/net/lib/CMakeLists.txt b/subsys/net/lib/CMakeLists.txt index aadddf332be7..46fffeede2fb 100644 --- a/subsys/net/lib/CMakeLists.txt +++ b/subsys/net/lib/CMakeLists.txt @@ -33,3 +33,4 @@ add_subdirectory_ifdef(CONFIG_WIFI_MGMT_EXT wifi_mgmt_ext) add_subdirectory_ifdef(CONFIG_MQTT_HELPER mqtt_helper) add_subdirectory_ifdef(CONFIG_NRF_PROVISIONING nrf_provisioning) add_subdirectory_ifdef(CONFIG_NRF_MCUMGR_SMP_CLIENT mcumgr_smp_client) +add_subdirectory_ifdef(CONFIG_WIFI_READY_LIB wifi_ready) diff --git a/subsys/net/lib/Kconfig b/subsys/net/lib/Kconfig index d73fae46c8ab..cd90f328ddad 100644 --- a/subsys/net/lib/Kconfig +++ b/subsys/net/lib/Kconfig @@ -45,5 +45,6 @@ rsource "wifi_mgmt_ext/Kconfig" rsource "mqtt_helper/Kconfig" rsource "nrf_provisioning/Kconfig" rsource "mcumgr_smp_client/Kconfig" +rsource "wifi_ready/Kconfig" endmenu diff --git a/subsys/net/lib/wifi_ready/CMakeLists.txt b/subsys/net/lib/wifi_ready/CMakeLists.txt new file mode 100644 index 000000000000..f757eb5ac460 --- /dev/null +++ b/subsys/net/lib/wifi_ready/CMakeLists.txt @@ -0,0 +1,8 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +zephyr_library_named(WIFI_READY_LIB) +zephyr_library_sources(wifi_ready.c) diff --git a/subsys/net/lib/wifi_ready/Kconfig b/subsys/net/lib/wifi_ready/Kconfig new file mode 100644 index 000000000000..9e90c442a158 --- /dev/null +++ b/subsys/net/lib/wifi_ready/Kconfig @@ -0,0 +1,35 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menuconfig WIFI_READY_LIB + bool "Wi-Fi ready event handling library" + select EXPERIMENTAL + depends on WPA_SUPP + depends on NET_MGMT + help + Enable WiFi ready management subsystem that allows the application to + register callbacks that are called when the WiFi is ready to be used and + when the WiFi is not ready to be used. + +if WIFI_READY_LIB + +module = WIFI_READY_LIB +module-str = wifi_ready_lib +source "subsys/logging/Kconfig.template.log_config" + +config WIFI_READY_MAX_CALLBACKS + int "Maximum number of Wi-Fi ready callbacks" + default 2 + help + Set the maximum number of Wi-Fi ready callbacks that can be registered + by the application. + +config WIFI_READY_INIT_PRIORITY + int "Wi-Fi ready initialization priority" + default 90 + help + Set the initialization priority of the Wi-Fi ready subsystem. +endif diff --git a/subsys/net/lib/wifi_ready/wifi_ready.c b/subsys/net/lib/wifi_ready/wifi_ready.c new file mode 100644 index 000000000000..cbe21e6e5631 --- /dev/null +++ b/subsys/net/lib/wifi_ready/wifi_ready.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +LOG_MODULE_REGISTER(wifi_ready, CONFIG_WIFI_READY_LIB_LOG_LEVEL); + +static wifi_ready_callback_t wifi_ready_callbacks[CONFIG_WIFI_READY_MAX_CALLBACKS]; +static unsigned char callback_count; +static K_MUTEX_DEFINE(wifi_ready_mutex); + +#define WPA_SUPP_EVENTS (NET_EVENT_WPA_SUPP_READY) | \ + (NET_EVENT_WPA_SUPP_NOT_READY) + +static struct net_mgmt_event_callback net_wpa_supp_cb; + +/* In case Wi-Fi is already ready, call the callbacks */ +static void wifi_ready_work_handler(struct k_work *item); + +static void call_wifi_ready_callbacks(bool ready, struct net_if *iface) +{ + int i; + + k_mutex_lock(&wifi_ready_mutex, K_FOREVER); + for (i = 0; i < ARRAY_SIZE(wifi_ready_callbacks); i++) { + if (wifi_ready_callbacks[i].wifi_ready_cb && + ((wifi_ready_callbacks[i].iface && + (wifi_ready_callbacks[i].iface == iface)) || + !wifi_ready_callbacks[i].iface)) { + wifi_ready_callbacks[i].wifi_ready_cb(ready); + } + } + k_mutex_unlock(&wifi_ready_mutex); +} + +static void wifi_ready_work_handler(struct k_work *item) +{ + wifi_ready_callback_t *cb = CONTAINER_OF(item, wifi_ready_callback_t, item); + + call_wifi_ready_callbacks(true, cb->iface); +} + +static void handle_wpa_supp_event(struct net_if *iface, bool ready) +{ + LOG_DBG("Supplicant event %s for iface %p", + ready ? "ready" : "not ready", iface); + + call_wifi_ready_callbacks(ready, iface); +} + +static void wpa_supp_event_handler(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, struct net_if *iface) +{ + ARG_UNUSED(cb); + + LOG_DBG("Event received: %d", mgmt_event); + switch (mgmt_event) { + case NET_EVENT_WPA_SUPP_READY: + handle_wpa_supp_event(iface, true); + break; + case NET_EVENT_WPA_SUPP_NOT_READY: + handle_wpa_supp_event(iface, false); + break; + default: + LOG_DBG("Unhandled event (%d)", mgmt_event); + break; + } +} + +int register_wifi_ready_callback(wifi_ready_callback_t cb, struct net_if *iface) +{ + int ret = 0, i; + unsigned int next_avail_idx = CONFIG_WIFI_READY_MAX_CALLBACKS; + struct net_if *wpas_iface = NULL; + + k_mutex_lock(&wifi_ready_mutex, K_FOREVER); + + if (!cb.wifi_ready_cb) { + LOG_ERR("Callback is NULL"); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(wifi_ready_callbacks); i++) { + if (wifi_ready_callbacks[i].wifi_ready_cb == NULL) { + next_avail_idx = i; + break; + } + } + + if (next_avail_idx == CONFIG_WIFI_READY_MAX_CALLBACKS) { + LOG_ERR("Reject callback registration, maximum count %d reached", + CONFIG_WIFI_READY_MAX_CALLBACKS); + ret = -ENOMEM; + goto out; + } + + /* Check if callback has already been registered for iface */ + for (i = 0; i < ARRAY_SIZE(wifi_ready_callbacks); i++) { + if (wifi_ready_callbacks[i].iface == iface && + wifi_ready_callbacks[i].wifi_ready_cb == cb.wifi_ready_cb) { + LOG_ERR("Callback has already registered for iface"); + ret = -EALREADY; + goto out; + } + } + + wifi_ready_callbacks[next_avail_idx].wifi_ready_cb = cb.wifi_ready_cb; + wifi_ready_callbacks[next_avail_idx].iface = iface; + + if (++callback_count == 1) { + net_mgmt_init_event_callback(&net_wpa_supp_cb, + wpa_supp_event_handler, WPA_SUPP_EVENTS); + net_mgmt_add_event_callback(&net_wpa_supp_cb); + } + + if (iface) { + wpas_iface = iface; + } else { + wpas_iface = net_if_get_first_wifi(); + if (!wpas_iface) { + LOG_ERR("Failed to get Wi-Fi interface"); + ret = -ENODEV; + goto out; + } + } + + /* In case Wi-Fi is already ready, call the callback */ + if (wifi_nm_get_instance_iface(wpas_iface)) { + k_work_submit(&wifi_ready_callbacks[next_avail_idx].item); + } + +out: + k_mutex_unlock(&wifi_ready_mutex); + return ret; + +} + + +int unregister_wifi_ready_callback(wifi_ready_callback_t cb, struct net_if *iface) +{ + int ret = 0, i; + bool found = false; + + k_mutex_lock(&wifi_ready_mutex, K_FOREVER); + + if (!cb.wifi_ready_cb) { + LOG_ERR("Callback is NULL"); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(wifi_ready_callbacks); i++) { + if (wifi_ready_callbacks[i].iface == iface && + wifi_ready_callbacks[i].wifi_ready_cb == cb.wifi_ready_cb) { + wifi_ready_callbacks[i].wifi_ready_cb = NULL; + wifi_ready_callbacks[i].iface = NULL; + found = true; + } + } + + if (!found) { + ret = -ENOENT; + goto out; + } + + if (--callback_count == 0) { + net_mgmt_del_event_callback(&net_wpa_supp_cb); + } + +out: + if (ret < 0) { + LOG_ERR("Failed to unregister callback: %d", ret); + } + k_mutex_unlock(&wifi_ready_mutex); + return ret; +} + +static int wifi_ready_init(void) +{ + /* Initialize the work items */ + for (int i = 0; i < ARRAY_SIZE(wifi_ready_callbacks); i++) { + k_work_init(&wifi_ready_callbacks[i].item, wifi_ready_work_handler); + } + + return 0; +} + +SYS_INIT(wifi_ready_init, APPLICATION, CONFIG_WIFI_READY_INIT_PRIORITY); diff --git a/west.yml b/west.yml index 984f5f551a1d..f95a26df8f66 100644 --- a/west.yml +++ b/west.yml @@ -142,7 +142,7 @@ manifest: - name: nrfxlib repo-path: sdk-nrfxlib path: nrfxlib - revision: 4bd894ac9a9fd32d1bcdddbded6be12f068013b6 + revision: 3cb1a198735889c7d01ece0aa295bc316b4840f8 - name: trusted-firmware-m repo-path: sdk-trusted-firmware-m path: modules/tee/tf-m/trusted-firmware-m