diff --git a/include/zephyr/net/wifi_mgmt.h b/include/zephyr/net/wifi_mgmt.h index a9dbd74607ca0..d5ca46d8b97ca 100644 --- a/include/zephyr/net/wifi_mgmt.h +++ b/include/zephyr/net/wifi_mgmt.h @@ -137,6 +137,8 @@ enum net_request_wifi_cmd { NET_REQUEST_WIFI_CMD_AP_WPS_CONFIG, /** Configure BSS maximum idle period */ NET_REQUEST_WIFI_CMD_BSS_MAX_IDLE_PERIOD, + /** Configure background scanning */ + NET_REQUEST_WIFI_CMD_BGSCAN, /** @cond INTERNAL_HIDDEN */ NET_REQUEST_WIFI_CMD_MAX /** @endcond */ @@ -332,6 +334,11 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_NEIGHBOR_REP_COMPLETE); NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_BSS_MAX_IDLE_PERIOD); +#define NET_REQUEST_WIFI_BGSCAN \ + (NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_BGSCAN) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_BGSCAN); + /** @cond INTERNAL_HIDDEN */ enum { @@ -1393,6 +1400,32 @@ enum wifi_sap_iface_state { WIFI_SAP_IFACE_ENABLED }; +#if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN) || defined(__DOXYGEN__) +/** @brief Wi-Fi background scan implementation */ +enum wifi_bgscan_type { + /** None, background scan is disabled */ + WIFI_BGSCAN_NONE = 0, + /** Simple, periodic scan based on signal strength */ + WIFI_BGSCAN_SIMPLE, + /** Learn channels used by the network (experimental) */ + WIFI_BGSCAN_LEARN, +}; + +/** @brief Wi-Fi background scan parameters */ +struct wifi_bgscan_params { + /** The type of background scanning */ + enum wifi_bgscan_type type; + /** Short scan interval in seconds */ + uint16_t short_interval; + /** Long scan interval in seconds */ + uint16_t long_interval; + /** Signal strength threshold in dBm */ + int8_t rssi_threshold; + /** Number of BSS Transition Management (BTM) queries */ + uint16_t btm_queries; +}; +#endif + /* Extended Capabilities */ enum wifi_ext_capab { WIFI_EXT_CAPAB_20_40_COEX = 0, @@ -1732,6 +1765,16 @@ struct wifi_mgmt_ops { */ int (*set_bss_max_idle_period)(const struct device *dev, unsigned short bss_max_idle_period); +#if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN) || defined(__DOXYGEN__) + /** Configure background scanning + * + * @param dev Pointer to the device structure for the driver instance. + * @param params Background scanning configuration parameters + * + * @return 0 if ok, < 0 if error + */ + int (*set_bgscan)(const struct device *dev, struct wifi_bgscan_params *params); +#endif }; /** Wi-Fi management offload API */ diff --git a/modules/hostap/CMakeLists.txt b/modules/hostap/CMakeLists.txt index f3233bb2cfbbc..106d31b8cca4b 100644 --- a/modules/hostap/CMakeLists.txt +++ b/modules/hostap/CMakeLists.txt @@ -78,6 +78,16 @@ zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WNM CONFIG_WNM ) +zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN + CONFIG_BGSCAN +) +zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN_SIMPLE + CONFIG_BGSCAN_SIMPLE +) +zephyr_library_compile_definitions_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN_LEARN + CONFIG_BGSCAN_LEARN +) + zephyr_library_include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/src ${HOSTAP_BASE}/ @@ -156,6 +166,16 @@ zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_WNM ${WIFI_NM_WPA_SUPPLICANT_BASE}/wnm_sta.c ) +zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN + ${WIFI_NM_WPA_SUPPLICANT_BASE}/bgscan.c +) +zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN_SIMPLE + ${WIFI_NM_WPA_SUPPLICANT_BASE}/bgscan_simple.c +) +zephyr_library_sources_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN_LEARN + ${WIFI_NM_WPA_SUPPLICANT_BASE}/bgscan_learn.c +) + zephyr_library_sources_ifdef(CONFIG_WPA_CLI src/wpa_cli.c ) diff --git a/modules/hostap/Kconfig b/modules/hostap/Kconfig index b29f76079e27c..098280337cda3 100644 --- a/modules/hostap/Kconfig +++ b/modules/hostap/Kconfig @@ -455,6 +455,27 @@ config WIFI_NM_WPA_SUPPLICANT_SKIP_DHCP_ON_ROAMING needs to get new IP address after roaming to new AP. Disable this to keep DHCP after roaming. +config WIFI_NM_WPA_SUPPLICANT_BGSCAN + bool "Background scanning (for legacy roaming), recommended if 802.11r is not supported" + depends on WIFI_NM_WPA_SUPPLICANT_WNM + depends on !WIFI_NM_WPA_SUPPLICANT_ROAMING + +if WIFI_NM_WPA_SUPPLICANT_BGSCAN + +config WIFI_NM_WPA_SUPPLICANT_BGSCAN_SIMPLE + bool "Simple background scanning" + default y + help + Periodic background scans based on signal strength. + +config WIFI_NM_WPA_SUPPLICANT_BGSCAN_LEARN + bool "Learning" + help + Learn channels used by the network and try to avoid + background scans on other channels (experimental). + +endif # WIFI_NM_WPA_SUPPLICANT_BGSCAN + # Create hidden config options that are used in hostap. This way we do not need # to mark them as allowed for CI checks, and also someone else cannot use the # same name options. @@ -486,6 +507,15 @@ config NO_RANDOM_POOL config WNM bool +config BGSCAN + bool + +config BGSCAN_SIMPLE + bool + +config BGSCAN_LEARN + bool + config NO_WPA bool default y if WIFI_NM_WPA_SUPPLICANT_CRYPTO_NONE diff --git a/modules/hostap/src/supp_api.c b/modules/hostap/src/supp_api.c index ea48e5259ea15..ed749d3351ff3 100644 --- a/modules/hostap/src/supp_api.c +++ b/modules/hostap/src/supp_api.c @@ -1907,6 +1907,71 @@ int supplicant_set_bss_max_idle_period(const struct device *dev, return wifi_mgmt_api->set_bss_max_idle_period(dev, bss_max_idle_period); } +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN +int supplicant_set_bgscan(const struct device *dev, struct wifi_bgscan_params *params) +{ + struct wpa_supplicant *wpa_s; + struct wpa_ssid *ssid; + int ret = -1; + + k_mutex_lock(&wpa_supplicant_mutex, K_FOREVER); + + wpa_s = get_wpa_s_handle(dev); + if (wpa_s == NULL) { + wpa_printf(MSG_ERROR, "Interface %s not found", dev->name); + goto out; + } + + ssid = wpa_s->current_ssid; + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "SSID for %s not found", dev->name); + goto out; + } + + switch (params->type) { + case WIFI_BGSCAN_SIMPLE: + if (!IS_ENABLED(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN_SIMPLE)) { + wpa_printf(MSG_ERROR, "Invalid bgscan type, enable " + "CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN_SIMPLE"); + ret = -ENOTSUP; + goto out; + } + if (!wpa_cli_cmd_v("set_network %d bgscan \"simple:%d:%d:%d:%d\"", ssid->id, + params->short_interval, params->rssi_threshold, + params->long_interval, params->btm_queries)) { + goto out; + } + break; + case WIFI_BGSCAN_LEARN: + if (!IS_ENABLED(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN_LEARN)) { + wpa_printf(MSG_ERROR, "Invalid bgscan type, enable " + "CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN_LEARN"); + ret = -ENOTSUP; + goto out; + } + if (!wpa_cli_cmd_v("set_network %d bgscan \"learn:%d:%d:%d\"", ssid->id, + params->short_interval, params->rssi_threshold, + params->long_interval)) { + goto out; + } + break; + case WIFI_BGSCAN_NONE: + default: + if (!wpa_cli_cmd_v("set_network %d bgscan \"\"", ssid->id)) { + goto out; + } + break; + } + + ret = 0; + +out: + k_mutex_unlock(&wpa_supplicant_mutex); + + return ret; +} +#endif + #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_WNM int supplicant_btm_query(const struct device *dev, uint8_t reason) { diff --git a/modules/hostap/src/supp_api.h b/modules/hostap/src/supp_api.h index 1ef63474f3fbc..8ab49f177e708 100644 --- a/modules/hostap/src/supp_api.h +++ b/modules/hostap/src/supp_api.h @@ -321,6 +321,17 @@ int supplicant_wps_config(const struct device *dev, struct wifi_wps_config_param */ int supplicant_set_bss_max_idle_period(const struct device *dev, unsigned short bss_max_idle_period); + +#if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN) || defined(__DOXYGEN__) +/** @ Set Wi-Fi background scanning parameters + * + * @param dev Wi-Fi interface handle to use + * @param params bgscan parameters + * @return 0 for OK; -1 for ERROR + */ +int supplicant_set_bgscan(const struct device *dev, struct wifi_bgscan_params *params); +#endif + #ifdef CONFIG_AP int set_ap_bandwidth(const struct device *dev, enum wifi_frequency_bandwidths bandwidth); diff --git a/modules/hostap/src/supp_main.c b/modules/hostap/src/supp_main.c index 74d195386b1fb..d1f8c360cb503 100644 --- a/modules/hostap/src/supp_main.c +++ b/modules/hostap/src/supp_main.c @@ -85,6 +85,9 @@ static const struct wifi_mgmt_ops mgmt_ops = { .get_conn_params = supplicant_get_wifi_conn_params, .wps_config = supplicant_wps_config, .set_bss_max_idle_period = supplicant_set_bss_max_idle_period, +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN + .set_bgscan = supplicant_set_bgscan, +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN */ #ifdef CONFIG_AP .ap_enable = supplicant_ap_enable, .ap_disable = supplicant_ap_disable, diff --git a/subsys/net/l2/wifi/wifi_mgmt.c b/subsys/net/l2/wifi/wifi_mgmt.c index 7145715973405..fbe8fa9a0ff12 100644 --- a/subsys/net/l2/wifi/wifi_mgmt.c +++ b/subsys/net/l2/wifi/wifi_mgmt.c @@ -1450,6 +1450,31 @@ static int wifi_set_bss_max_idle_period(uint64_t mgmt_request, struct net_if *if NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_BSS_MAX_IDLE_PERIOD, wifi_set_bss_max_idle_period); +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN +static int wifi_set_bgscan(uint64_t mgmt_request, struct net_if *iface, void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_api(iface); + struct wifi_bgscan_params *params = data; + + if (wifi_mgmt_api == NULL || wifi_mgmt_api->set_bgscan == NULL) { + return -ENOTSUP; + } + + if (!net_if_is_admin_up(iface)) { + return -ENETDOWN; + } + + if (data == NULL || len != sizeof(*params)) { + return -EINVAL; + } + + return wifi_mgmt_api->set_bgscan(dev, params); +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_BGSCAN, wifi_set_bgscan); +#endif + #ifdef CONFIG_WIFI_MGMT_RAW_SCAN_RESULTS void wifi_mgmt_raise_raw_scan_result_event(struct net_if *iface, struct wifi_raw_scan_result *raw_scan_result) diff --git a/subsys/net/l2/wifi/wifi_shell.c b/subsys/net/l2/wifi/wifi_shell.c index 555358aff0a9c..5aaabebf697a5 100644 --- a/subsys/net/l2/wifi/wifi_shell.c +++ b/subsys/net/l2/wifi/wifi_shell.c @@ -3555,6 +3555,113 @@ static int cmd_wifi_set_bss_max_idle_period(const struct shell *sh, size_t argc, return 0; } +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN +static int wifi_bgscan_args_to_params(const struct shell *sh, size_t argc, char *argv[], + struct wifi_bgscan_params *params) +{ + int err; + int opt; + int opt_index = 0; + struct getopt_state *state; + static const struct option long_options[] = { + {"type", required_argument, 0, 't'}, + {"short-interval", required_argument, 0, 's'}, + {"rss-threshold", required_argument, 0, 'r'}, + {"long-interval", required_argument, 0, 'l'}, + {"btm-queries", required_argument, 0, 'b'}, + {"iface", required_argument, 0, 'i'}, + {0, 0, 0, 0}}; + unsigned long uval; + long val; + + while ((opt = getopt_long(argc, argv, "t:s:r:l:b:i:", long_options, &opt_index)) != -1) { + state = getopt_state_get(); + switch (opt) { + case 't': + if (strcmp("simple", state->optarg) == 0) { + params->type = WIFI_BGSCAN_SIMPLE; + } else if (strcmp("learn", state->optarg) == 0) { + params->type = WIFI_BGSCAN_LEARN; + } else if (strcmp("none", state->optarg) == 0) { + params->type = WIFI_BGSCAN_NONE; + } else { + PR_ERROR("Invalid type %s\n", state->optarg); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + break; + case 's': + uval = shell_strtoul(state->optarg, 10, &err); + if (err < 0) { + PR_ERROR("Invalid short interval %s\n", state->optarg); + return err; + } + params->short_interval = uval; + break; + case 'l': + uval = shell_strtoul(state->optarg, 10, &err); + if (err < 0) { + PR_ERROR("Invalid long interval %s\n", state->optarg); + return err; + } + params->long_interval = uval; + break; + case 'b': + uval = shell_strtoul(state->optarg, 10, &err); + if (err < 0) { + PR_ERROR("Invalid BTM queries %s\n", state->optarg); + return err; + } + params->btm_queries = uval; + break; + case 'r': + val = shell_strtol(state->optarg, 10, &err); + if (err < 0) { + PR_ERROR("Invalid RSSI threshold %s\n", state->optarg); + return err; + } + params->rssi_threshold = val; + break; + case 'i': + /* Unused, but parsing to avoid unknown option error */ + break; + default: + PR_ERROR("Invalid option %c\n", state->optopt); + shell_help(sh); + return SHELL_CMD_HELP_PRINTED; + } + } + + return 0; +} + +static int cmd_wifi_set_bgscan(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = get_iface(IFACE_TYPE_STA, argc, argv); + struct wifi_bgscan_params bgscan_params = { + .type = WIFI_BGSCAN_NONE, + .short_interval = 30, + .long_interval = 300, + .rssi_threshold = 0, + .btm_queries = 0, + }; + int ret; + + if (wifi_bgscan_args_to_params(sh, argc, argv, &bgscan_params) != 0) { + return -ENOEXEC; + } + + ret = net_mgmt(NET_REQUEST_WIFI_BGSCAN, iface, &bgscan_params, + sizeof(struct wifi_bgscan_params)); + if (ret != 0) { + PR_WARNING("Setting background scanning parameters failed: %s\n", strerror(-ret)); + return -ENOEXEC; + } + + return 0; +} +#endif + static int wifi_config_args_to_params(const struct shell *sh, size_t argc, char *argv[], struct wifi_config_params *params) { @@ -4098,6 +4205,19 @@ SHELL_SUBCMD_ADD((wifi), bss_max_idle_period, NULL, cmd_wifi_set_bss_max_idle_period, 2, 2); +#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN +SHELL_SUBCMD_ADD((wifi), bgscan, NULL, + "Configure background scanning.\n" + "<-t, --type simple/learn/none> : The scanning type, use none to disable.\n" + "[-s, --short-interval ] : Short scan interval (default: 30).\n" + "[-l, --long-interval ] : Long scan interval (default: 300).\n" + "[-r, --rssi-threshold ] : Signal strength threshold (default: disabled).\n" + "[-b, --btm-queries ] : BTM queries before scanning (default: disabled).\n" + "[-i, --iface=] : Interface index.\n", + cmd_wifi_set_bgscan, + 2, 6); +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_BGSCAN */ + SHELL_SUBCMD_ADD((wifi), config, NULL, "Configure STA parameters.\n" "-o, --okc=<0/1>: Opportunistic Key Caching. 0: disable, 1: enable\n" diff --git a/west.yml b/west.yml index e18bf7eab171d..d1279aea8c039 100644 --- a/west.yml +++ b/west.yml @@ -286,7 +286,7 @@ manifest: - hal - name: hostap path: modules/lib/hostap - revision: 61182a45fecafaa0f20e98ca7f862d26fbf65293 + revision: 3ec675be30c25b56cc0e7dbd5bd931a87d32937e - name: liblc3 revision: 48bbd3eacd36e99a57317a0a4867002e0b09e183 path: modules/lib/liblc3