From c3f1c3fcdef2b14a358e494d5beb9a36a252f0bd Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Fri, 29 Mar 2024 17:58:12 +0530 Subject: [PATCH 01/12] feat(esp_wifi): Add channel bitmaps to the scan config - Add the ability to specify the specific channels and the in wifi_config_t. - Adds warning that discourages setting bssid and channel in sta_config when btm/mbo is enabled and supported by the network. - Fix issues with reporting authmodes of FT supporting APs during scanning. - Additional typo fixes in the public header files --- .../esp_wifi/include/esp_wifi_types_generic.h | 53 ++++++++++++------- components/esp_wifi/lib | 2 +- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/components/esp_wifi/include/esp_wifi_types_generic.h b/components/esp_wifi/include/esp_wifi_types_generic.h index 83e0043d7c..7f0a1b40ab 100644 --- a/components/esp_wifi/include/esp_wifi_types_generic.h +++ b/components/esp_wifi/include/esp_wifi_types_generic.h @@ -164,15 +164,24 @@ typedef struct { cause station to disconnect from AP and are not recommended. */ } wifi_scan_time_t; +typedef struct { + uint16_t ghz_2_channels; /**< Represents 2.4 GHz channels */ + uint8_t unii1_channels; /**< UNII-1 channels */ + uint8_t unii2_channels; /**< UNII-2 channels */ + uint16_t unii2e_channels; /**< UNII-2 Extended channels */ + uint8_t unii3_channels; /**< UNII-3 channels */ +} wifi_scan_channel_bitmap_t; + /** @brief Parameters for an SSID scan. */ typedef struct { - uint8_t *ssid; /**< SSID of AP */ - uint8_t *bssid; /**< MAC address of AP */ - uint8_t channel; /**< channel, scan the specific channel */ - bool show_hidden; /**< enable to scan AP whose SSID is hidden */ - wifi_scan_type_t scan_type; /**< scan type, active or passive */ - wifi_scan_time_t scan_time; /**< scan time per channel */ - uint8_t home_chan_dwell_time;/**< time spent at home channel between scanning consecutive channels.*/ + uint8_t *ssid; /**< SSID of AP */ + uint8_t *bssid; /**< MAC address of AP */ + uint8_t channel; /**< channel, scan the specific channel */ + bool show_hidden; /**< enable to scan AP whose SSID is hidden */ + wifi_scan_type_t scan_type; /**< scan type, active or passive */ + wifi_scan_time_t scan_time; /**< scan time per channel */ + uint8_t home_chan_dwell_time; /**< time spent at home channel between scanning consecutive channels. */ + wifi_scan_channel_bitmap_t channel_bitmap; /**< Channel bitmap for setting specific channels to be scanned. For 2.4ghz channels set ghz_2_channels from BIT(1) to BIT(14) from LSB to MSB order to indicate channels to be scanned. Currently scanning in 5ghz channels is not supported. Please note that the 'channel' parameter above needs to be set to 0 to allow scanning by bitmap. */ } wifi_scan_config_t; typedef enum { @@ -247,7 +256,7 @@ typedef enum { typedef struct { int8_t rssi; /**< The minimum rssi to accept in the fast scan mode */ wifi_auth_mode_t authmode; /**< The weakest authmode to accept in the fast scan mode - Note: Incase this value is not set and password is set as per WPA2 standards(password len >= 8), it will be defaulted to WPA2 and device won't connect to deprecated WEP/WPA networks. Please set authmode threshold as WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK to connect to WEP/WPA networks */ + Note: In case this value is not set and password is set as per WPA2 standards(password len >= 8), it will be defaulted to WPA2 and device won't connect to deprecated WEP/WPA networks. Please set authmode threshold as WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK to connect to WEP/WPA networks */ }wifi_scan_threshold_t; typedef enum { @@ -269,8 +278,8 @@ typedef enum { /** Configuration structure for Protected Management Frame */ typedef struct { - bool capable; /**< Deprecated variable. Device will always connect in PMF mode if other device also advertizes PMF capability. */ - bool required; /**< Advertizes that Protected Management Frame is required. Device will not associate to non-PMF capable devices. */ + bool capable; /**< Deprecated variable. Device will always connect in PMF mode if other device also advertises PMF capability. */ + bool required; /**< Advertises that Protected Management Frame is required. Device will not associate to non-PMF capable devices. */ } wifi_pmf_config_t; /** Configuration for SAE PWE derivation */ @@ -318,8 +327,8 @@ typedef struct { wifi_scan_threshold_t threshold; /**< When scan_threshold is set, only APs which have an auth mode that is more secure than the selected auth mode and a signal stronger than the minimum RSSI will be used. */ wifi_pmf_config_t pmf_cfg; /**< Configuration for Protected Management Frame. Will be advertised in RSN Capabilities in RSN IE. */ uint32_t rm_enabled:1; /**< Whether Radio Measurements are enabled for the connection */ - uint32_t btm_enabled:1; /**< Whether BSS Transition Management is enabled for the connection */ - uint32_t mbo_enabled:1; /**< Whether MBO is enabled for the connection */ + uint32_t btm_enabled:1; /**< Whether BSS Transition Management is enabled for the connection. Note that when btm is enabled, the application itself should not set specific bssid (i.e using bssid_set and bssid in this config)or channel to connect to. This defeats the purpose of a BTM supported network, and hence if btm is supported and a specific bssid or channel is set in this config, it will be cleared from the config at the first disconnection or connection so that the device can roam to other BSS. It is recommended not to set BSSID when BTM is enabled. */ + uint32_t mbo_enabled:1; /**< Whether MBO is enabled for the connection. Note that when mbo is enabled, the application itself should not set specific bssid (i.e using bssid_set and bssid in this config)or channel to connect to. This defeats the purpose of a MBO supported network, and hence if btm is supported and a specific bssid or channel is set in this config, it will be cleared from the config at the first disconnection or connection so that the device can roam to other BSS. It is recommended not to set BSSID when MBO is enabled. Enabling mbo here, automatically enables btm and rm above.*/ uint32_t ft_enabled:1; /**< Whether FT is enabled for the connection */ uint32_t owe_enabled:1; /**< Whether OWE is enabled for the connection */ uint32_t transition_disable:1; /**< Whether to enable transition disable feature */ @@ -327,7 +336,7 @@ typedef struct { wifi_sae_pwe_method_t sae_pwe_h2e; /**< Configuration for SAE PWE derivation method */ wifi_sae_pk_mode_t sae_pk_mode; /**< Configuration for SAE-PK (Public Key) Authentication method */ uint8_t failure_retry_cnt; /**< Number of connection retries station will do before moving to next AP. scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config. - Note: Enabling this may cause connection time to increase incase best AP doesn't behave properly. */ + Note: Enabling this may cause connection time to increase in case best AP doesn't behave properly. */ uint32_t he_dcm_set:1; /**< Whether DCM max.constellation for transmission and reception is set. */ uint32_t he_dcm_max_constellation_tx:2; /**< Indicate the max.constellation for DCM in TB PPDU the STA supported. 0: not supported. 1: BPSK, 2: QPSK, 3: 16-QAM. The default value is 3. */ uint32_t he_dcm_max_constellation_rx:2; /**< Indicate the max.constellation for DCM in both Data field and HE-SIG-B field the STA supported. 0: not supported. 1: BPSK, 2: QPSK, 3: 16-QAM. The default value is 3. */ @@ -441,7 +450,7 @@ typedef struct { typedef enum { WIFI_PKT_MGMT, /**< Management frame, indicates 'buf' argument is wifi_promiscuous_pkt_t */ WIFI_PKT_CTRL, /**< Control frame, indicates 'buf' argument is wifi_promiscuous_pkt_t */ - WIFI_PKT_DATA, /**< Data frame, indiciates 'buf' argument is wifi_promiscuous_pkt_t */ + WIFI_PKT_DATA, /**< Data frame, indicates 'buf' argument is wifi_promiscuous_pkt_t */ WIFI_PKT_MISC, /**< Other type, such as MIMO etc. 'buf' argument is wifi_promiscuous_pkt_t but the payload is zero length. */ } wifi_promiscuous_pkt_type_t; @@ -565,10 +574,10 @@ typedef struct { #define ESP_WIFI_NDP_ROLE_INITIATOR 1 #define ESP_WIFI_NDP_ROLE_RESPONDER 2 -#define ESP_WIFI_MAX_SVC_NAME_LEN 256 -#define ESP_WIFI_MAX_FILTER_LEN 256 -#define ESP_WIFI_MAX_SVC_INFO_LEN 64 - +#define ESP_WIFI_MAX_SVC_NAME_LEN 256 +#define ESP_WIFI_MAX_FILTER_LEN 256 +#define ESP_WIFI_MAX_SVC_INFO_LEN 64 +#define ESP_WIFI_MAX_NEIGHBOR_REP_LEN 64 /** * @brief NAN Services types * @@ -781,6 +790,8 @@ typedef enum { WIFI_EVENT_NDP_TERMINATED, /**< NAN Datapath terminated indication */ WIFI_EVENT_HOME_CHANNEL_CHANGE, /**< WiFi home channel change,doesn't occur when scanning */ + WIFI_EVENT_STA_NEIGHBOR_REP, /**< Received Neighbor Report response */ + WIFI_EVENT_MAX, /**< Invalid WiFi event ID */ } wifi_event_t; @@ -1009,6 +1020,12 @@ typedef struct { uint8_t init_ndi[6]; /**< Initiator's NAN Data Interface MAC */ } wifi_event_ndp_terminated_t; +/** Argument structure for WIFI_EVENT_STA_NEIGHBOR_REP event */ +typedef struct { + uint8_t report[ESP_WIFI_MAX_NEIGHBOR_REP_LEN]; /**< Neighbor Report received from the AP*/ + uint16_t report_len; /**< Length of the report*/ +} wifi_event_neighbor_report_t; + #ifdef __cplusplus } #endif diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index 5ae2e01957..8fde018db0 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit 5ae2e01957e3d1e38f5e423a25bd241d3a6ef339 +Subproject commit 8fde018db003c7918802d31d14a22eea58c9ba86 From 743772fb76c9a8a7347076dabea6984128cfa121 Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Thu, 31 Aug 2023 18:21:50 +0530 Subject: [PATCH 02/12] fix(btm): Improve BTM scanning effiency by using channel bitmap - Improve the BTM scanning efficiency by using channel bitmap feature in scanning. This sets only the channels we need to scan instead of all. --- .../esp_supplicant/src/esp_scan.c | 20 ++++++++++++++++++- components/wpa_supplicant/src/utils/common.h | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_scan.c b/components/wpa_supplicant/esp_supplicant/src/esp_scan.c index b0192bb94b..0a7c27f8b4 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_scan.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_scan.c @@ -166,7 +166,21 @@ int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender, return 0; } +#ifdef CONFIG_WNM +void get_scan_channel_bitmap(struct wpa_supplicant *wpa_s, wifi_scan_config_t *params) { + if (!wpa_s->wnm_num_neighbor_report) { + wpa_printf(MSG_DEBUG, "No Neighbor Report to gather scan channel list"); + return; + } + params->channel_bitmap.ghz_2_channels = 0; + for (int i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + nei = &wpa_s->wnm_neighbor_report_elements[i]; + params->channel_bitmap.ghz_2_channels |= (1 << nei->channel_number); + } +} +#endif /*CONFIG_WNM*/ static int issue_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *scan_params) { @@ -217,7 +231,11 @@ static int issue_scan(struct wpa_supplicant *wpa_s, if (scan_params->channel) { params->channel = scan_params->channel; } - +#ifdef CONFIG_WNM + else { + get_scan_channel_bitmap(wpa_s, params); + } +#endif /*CONFIG_WNM*/ if (scan_params->duration) { params->scan_time.passive = scan_params->duration; params->scan_time.active.min = scan_params->duration; diff --git a/components/wpa_supplicant/src/utils/common.h b/components/wpa_supplicant/src/utils/common.h index efd34a3f02..655de8177a 100644 --- a/components/wpa_supplicant/src/utils/common.h +++ b/components/wpa_supplicant/src/utils/common.h @@ -479,3 +479,5 @@ void * __hide_aliasing_typecast(void *foo); #define IANA_SECP384R1 20 #define IANA_SECP521R1 21 #endif /* COMMON_H */ + +#define GHZ_24_MAX_CHANNEL_COUNT 14 From c6134a23dd9ae3379c4f0e4fa912d50fb6557ac3 Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Fri, 29 Sep 2023 15:19:08 +0530 Subject: [PATCH 03/12] fix(wpa_supplicant): Replace Neighbor Report callback with an event - Deprecate the existing esp_rrm_send_neighbor_rep_request() API - Adds a new API to send neighbor report requests esp_rrm_send_neighbor_report_request(). This replaces the older API's callback procedure with a new Wi-Fi event that is posted when the neighbor report is received. This moves the execution of the callback from supplicant context to freertos context. --- .../esp_supplicant/include/esp_rrm.h | 16 +++++- .../esp_supplicant/src/esp_common.c | 50 ++++++++++++++++++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/components/wpa_supplicant/esp_supplicant/include/esp_rrm.h b/components/wpa_supplicant/esp_supplicant/include/esp_rrm.h index fa40e0cd62..bd2fabfc8c 100644 --- a/components/wpa_supplicant/esp_supplicant/include/esp_rrm.h +++ b/components/wpa_supplicant/esp_supplicant/include/esp_rrm.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -27,6 +27,8 @@ typedef void (*neighbor_rep_request_cb)(void *ctx, const uint8_t *report, size_t /** * @brief Send Radio measurement neighbor report request to connected AP * + * @deprecated This function is deprecated and will be removed in the future. + * Please use 'esp_rrm_send_neighbor_report_request' * @param cb: callback function for neighbor report * @param cb_ctx: callback context * @@ -35,8 +37,18 @@ typedef void (*neighbor_rep_request_cb)(void *ctx, const uint8_t *report, size_t * - -1: AP does not support RRM * - -2: station not connected to AP */ + +__attribute__((deprecated("Use 'esp_rrm_send_neighbor_report_request' instead"))) int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb, - void *cb_ctx); + void *cb_ctx); +/** + * @brief Send Radio measurement neighbor report request to connected AP + * @return + * - 0: success + * - -1: AP does not support RRM + * - -2: station not connected to AP + */ +int esp_rrm_send_neighbor_report_request(void); /** * @brief Check RRM capability of connected AP diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_common.c b/components/wpa_supplicant/esp_supplicant/src/esp_common.c index 0f9e8f540c..85809805f1 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_common.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_common.c @@ -477,6 +477,7 @@ bool esp_rrm_is_rrm_supported_connection(void) return true; } +/*This function has been deprecated in favour of esp_rrm_send_neighbor_report_request*/ int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb, void *cb_ctx) { @@ -503,6 +504,49 @@ int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb, return wpas_rrm_send_neighbor_rep_request(wpa_s, &wpa_ssid, 0, 0, cb, cb_ctx); } + +void neighbor_report_recvd_cb(void *ctx, const uint8_t *report, size_t report_len) { + if (report == NULL) { + wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE"); + esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, NULL, 0, 0); + return; + } + if (report_len > ESP_WIFI_MAX_NEIGHBOR_REP_LEN) { + wpa_printf(MSG_ERROR, "RRM: Neighbor report too large (>%d bytes), hence not reporting", ESP_WIFI_MAX_NEIGHBOR_REP_LEN); + return; + } + wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)", report[0]); + wifi_event_neighbor_report_t neighbor_report_event = {0}; + os_memcpy(neighbor_report_event.report, report, report_len); + neighbor_report_event.report_len = report_len; + esp_event_post(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, &neighbor_report_event, sizeof(wifi_event_neighbor_report_t), 0); +} + +int esp_rrm_send_neighbor_report_request(void) +{ + struct wpa_supplicant *wpa_s = &g_wpa_supp; + struct wpa_ssid_value wpa_ssid = {0}; + struct wifi_ssid *ssid; + + if (!wpa_s->current_bss) { + wpa_printf(MSG_ERROR, "STA not associated, return"); + return -2; + } + + if (!(wpa_s->rrm_ie[0] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { + wpa_printf(MSG_ERROR, + "RRM: No network support for Neighbor Report."); + return -1; + } + + ssid = esp_wifi_sta_get_prof_ssid_internal(); + + os_memcpy(wpa_ssid.ssid, ssid->ssid, ssid->len); + wpa_ssid.ssid_len = ssid->len; + + return wpas_rrm_send_neighbor_rep_request(wpa_s, &wpa_ssid, 0, 0, neighbor_report_recvd_cb, NULL); +} + bool esp_wnm_is_btm_supported_connection(void) { struct wpa_supplicant *wpa_s = &g_wpa_supp; @@ -847,12 +891,16 @@ int esp_supplicant_post_evt(uint32_t evt_id, uint32_t data) } #endif /* CONFIG_SUPPLICANT_TASK */ #else /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */ +int esp_rrm_send_neighbor_report_request(void) +{ + return -1; +} + int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb, void *cb_ctx) { return -1; } - int esp_wnm_send_bss_transition_mgmt_query(enum btm_query_reason query_reason, const char *btm_candidates, int cand_list) From ff622e44289d3a7be837d93485fba2ce290d1a76 Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Fri, 29 Sep 2023 15:19:40 +0530 Subject: [PATCH 04/12] fix(wpa_supplicant): Apply the Neighbor report event in roaming example - Apply the neighbor report event structure in the roaming example. - This replaces the existing callback structure that existed earlier. --- examples/wifi/roaming/main/roaming_example.c | 116 +++++++++++++------ 1 file changed, 79 insertions(+), 37 deletions(-) diff --git a/examples/wifi/roaming/main/roaming_example.c b/examples/wifi/roaming/main/roaming_example.c index 367114d52b..fce9fbe0d9 100644 --- a/examples/wifi/roaming/main/roaming_example.c +++ b/examples/wifi/roaming/main/roaming_example.c @@ -13,14 +13,14 @@ #include "esp_mac.h" #include "nvs_flash.h" #include "esp_netif.h" +#include /* Configuration */ #define EXAMPLE_WIFI_SSID CONFIG_EXAMPLE_WIFI_SSID #define EXAMPLE_WIFI_PASSWORD CONFIG_EXAMPLE_WIFI_PASSWORD #define EXAMPLE_WIFI_RSSI_THRESHOLD CONFIG_EXAMPLE_WIFI_RSSI_THRESHOLD -/* rrm ctx */ -int rrm_ctx = 0; +static bool g_neighbor_report_active; /* FreeRTOS event group to signal when we are connected & ready to make a request */ static EventGroupHandle_t wifi_event_group; @@ -57,6 +57,8 @@ static void event_handler(void* arg, esp_event_base_t event_base, #endif if (esp_rrm_is_rrm_supported_connection()) { ESP_LOGI(TAG,"RRM supported"); + esp_rrm_send_neighbor_report_request(); + g_neighbor_report_active = true; } else { ESP_LOGI(TAG,"RRM not supported"); } @@ -85,6 +87,7 @@ static void event_handler(void* arg, esp_event_base_t event_base, #define ETH_ALEN 6 #endif +#define MAX_LCI_CIVIC_LEN 256 * 2 + 1 #define MAX_NEIGHBOR_LEN 512 #if 1 static char * get_btm_neighbor_list(uint8_t *report, size_t report_len) @@ -93,6 +96,8 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len) const uint8_t *data; int ret = 0; + char *lci = NULL; + char *civic = NULL; /* * Neighbor Report element (IEEE P802.11-REVmc/D5.0) * BSSID[6] @@ -110,12 +115,24 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len) } char *buf = calloc(1, MAX_NEIGHBOR_LEN); + if (!buf) { + ESP_LOGE(TAG, "Memory allocation for neighbor list failed"); + goto cleanup; + } data = report; while (report_len >= 2 + NR_IE_MIN_LEN) { const uint8_t *nr; - char lci[256 * 2 + 1]; - char civic[256 * 2 + 1]; + lci = (char *)malloc(sizeof(char)*MAX_LCI_CIVIC_LEN); + if (!lci) { + ESP_LOGE(TAG, "Memory allocation for lci failed"); + goto cleanup; + } + civic = (char *)malloc(sizeof(char)*MAX_LCI_CIVIC_LEN); + if (!civic) { + ESP_LOGE(TAG, "Memory allocation for civic failed"); + goto cleanup; + } uint8_t nr_len = data[1]; const uint8_t *pos = data, *end; @@ -201,9 +218,25 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len) data = end; report_len -= 2 + nr_len; + + if (lci) { + free(lci); + lci = NULL; + } + if (civic) { + free(civic); + civic = NULL; + } } cleanup: + if (lci) { + free(lci); + } + if (civic) { + free(civic); + } + if (ret < 0) { free(buf); buf = NULL; @@ -274,46 +307,50 @@ char * get_tmp_neighbor_list(uint8_t *report, size_t report_len) return buf; } #endif - -void neighbor_report_recv_cb(void *ctx, const uint8_t *report, size_t report_len) +static void esp_neighbor_report_recv_handler(void* arg, esp_event_base_t event_base,int32_t event_id, void* event_data) { - int *val = ctx; - uint8_t *pos = (uint8_t *)report; - int cand_list = 0; + if (!g_neighbor_report_active) { + ESP_LOGV(TAG,"Neighbor report recieved but not triggerred by us"); + return; + } + if (!event_data) { + ESP_LOGE(TAG, "No event data recieved for neighbor report"); + return; + } + g_neighbor_report_active = false; + uint8_t cand_list = 0; + wifi_event_neighbor_report_t *neighbor_report_event = (wifi_event_neighbor_report_t*)event_data; + uint8_t *pos = (uint8_t *)neighbor_report_event->report; + char * neighbor_list = NULL; + if (!pos) { + ESP_LOGE(TAG, "Neighbor report is empty"); + return; + } + uint8_t report_len = neighbor_report_event->report_len; + /* dump report info */ + ESP_LOGD(TAG, "rrm: neighbor report len=%d", report_len); + ESP_LOG_BUFFER_HEXDUMP(TAG, pos, report_len, ESP_LOG_DEBUG); - if (!report) { - ESP_LOGE(TAG, "report is null"); - return; - } - if (*val != rrm_ctx) { - ESP_LOGE(TAG, "rrm_ctx value didn't match, not initiated by us"); - return; - } - /* dump report info */ - ESP_LOGI(TAG, "rrm: neighbor report len=%d", report_len); - ESP_LOG_BUFFER_HEXDUMP(TAG, pos, report_len, ESP_LOG_INFO); - - /* create neighbor list */ - char *neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1); - - /* In case neighbor list is not present issue a scan and get the list from that */ - if (!neighbor_list) { - /* issue scan */ - wifi_scan_config_t params; - memset(¶ms, 0, sizeof(wifi_scan_config_t)); - if (esp_wifi_scan_start(¶ms, true) < 0) { - goto cleanup; - } - /* cleanup from net802.11 */ - esp_wifi_clear_ap_list(); - cand_list = 1; + /* create neighbor list */ + neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1); + /* In case neighbor list is not present issue a scan and get the list from that */ + if (!neighbor_list) { + /* issue scan */ + wifi_scan_config_t params; + memset(¶ms, 0, sizeof(wifi_scan_config_t)); + if (esp_wifi_scan_start(¶ms, true) < 0) { + goto cleanup; + } + /* cleanup from net802.11 */ + esp_wifi_clear_ap_list(); + cand_list = 1; } /* send AP btm query, this will cause STA to roam as well */ esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, neighbor_list, cand_list); - cleanup: if (neighbor_list) free(neighbor_list); + } #if EXAMPLE_WIFI_RSSI_THRESHOLD @@ -325,13 +362,16 @@ static void esp_bss_rssi_low_handler(void* arg, esp_event_base_t event_base, ESP_LOGI(TAG, "%s:bss rssi is=%d", __func__, event->rssi); /* Lets check channel conditions */ rrm_ctx++; - if (esp_rrm_send_neighbor_rep_request(neighbor_report_recv_cb, &rrm_ctx) < 0) { + if (esp_rrm_send_neighbor_report_request() < 0) { /* failed to send neighbor report request */ ESP_LOGI(TAG, "failed to send neighbor report request"); if (esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, NULL, 0) < 0) { ESP_LOGI(TAG, "failed to send btm query"); } + } else { + g_neighbor_report_active = true; } + } #endif @@ -378,6 +418,8 @@ static void initialise_wifi(void) ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) ); ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) ); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, + &esp_neighbor_report_recv_handler, NULL)); #if EXAMPLE_WIFI_RSSI_THRESHOLD ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, &esp_bss_rssi_low_handler, NULL)); From 84d7ab5c0c8d4574503828dde6a26492638af7eb Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Fri, 29 Sep 2023 11:32:55 +0530 Subject: [PATCH 05/12] fix(wpa_supplicant): Clear bssid flag and channel in supplicant disconnect handler - Clear the bssid set flag and channel in supplicant disconnect handler as this can cause the station to recursively connect to the wrong AP in case roaming through BTM mechanisms fails. - Fix issue with incorrect blocking time calculation when blocking scan issued for a single channel. --- .../esp_supplicant/src/esp_common.c | 15 +++++++++++---- .../esp_supplicant/src/esp_common_i.h | 2 +- .../esp_supplicant/src/esp_wpa_main.c | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_common.c b/components/wpa_supplicant/esp_supplicant/src/esp_common.c index 85809805f1..e6dac19a54 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_common.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_common.c @@ -162,7 +162,7 @@ static void btm_rrm_task(void *pvParameters) } #endif /* CONFIG_SUPPLICANT_TASK */ -static void clear_bssid_flag(struct wpa_supplicant *wpa_s) +static void clear_bssid_flag_and_channel(struct wpa_supplicant *wpa_s) { wifi_config_t *config; @@ -178,7 +178,8 @@ static void clear_bssid_flag(struct wpa_supplicant *wpa_s) } esp_wifi_get_config(WIFI_IF_STA, config); - if (config->sta.bssid_set) { + if (config->sta.bssid_set || config->sta.channel) { + config->sta.channel = 0; config->sta.bssid_set = 0; esp_wifi_set_config(WIFI_IF_STA, config); } @@ -439,11 +440,11 @@ void supplicant_sta_conn_handler(uint8_t *bssid) /* Register for mgmt frames */ register_mgmt_frames(wpa_s); /* clear set bssid flag */ - clear_bssid_flag(wpa_s); + clear_bssid_flag_and_channel(wpa_s); #endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */ } -void supplicant_sta_disconn_handler(void) +void supplicant_sta_disconn_handler(uint8_t reason_code) { #if defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) struct wpa_supplicant *wpa_s = &g_wpa_supp; @@ -451,6 +452,12 @@ void supplicant_sta_disconn_handler(void) #ifdef CONFIG_IEEE80211KV wpas_rrm_reset(wpa_s); wpas_clear_beacon_rep_data(wpa_s); + /* Not clearing in case of roaming disconnect as BTM induced connection + * itself sets a specific bssid and channel to connect to before disconnection. + * Subsequent connections or disconnections will clear this flag */ + if (reason_code != WIFI_REASON_ROAMING) { + clear_bssid_flag_and_channel(wpa_s); + } #endif /* CONFIG_IEEE80211KV */ if (wpa_s->current_bss) { wpa_s->current_bss = NULL; diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_common_i.h b/components/wpa_supplicant/esp_supplicant/src/esp_common_i.h index e6c1d23290..3f4a439bbb 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_common_i.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_common_i.h @@ -46,5 +46,5 @@ void esp_supplicant_unset_all_appie(void); void esp_set_scan_ie(void); void esp_set_assoc_ie(uint8_t *bssid, const u8 *ies, size_t ies_len, bool add_mdie); void supplicant_sta_conn_handler(uint8_t* bssid); -void supplicant_sta_disconn_handler(void); +void supplicant_sta_disconn_handler(uint8_t reason_code); #endif diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c index c977119922..acde48189b 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c @@ -301,7 +301,7 @@ static void wpa_sta_disconnected_cb(uint8_t reason_code) owe_deinit(); #endif /* CONFIG_OWE_STA */ - supplicant_sta_disconn_handler(); + supplicant_sta_disconn_handler(reason_code); } #ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT From 560c951e33726cd41c10f650c16e3bcf02e4d171 Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Fri, 29 Sep 2023 14:14:44 +0530 Subject: [PATCH 06/12] fix(wpa_supplicant): Add default durations to supplicant issued scans - Add default durations to supplicant issued scans based on results from initial experiments where probe response times were recorded in a significantly noisy environment. It was noticed that within 70ms we receive over 80% of the responses 90% of the time --- components/wpa_supplicant/esp_supplicant/src/esp_scan.c | 3 +++ components/wpa_supplicant/esp_supplicant/src/esp_scan_i.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_scan.c b/components/wpa_supplicant/esp_supplicant/src/esp_scan.c index 0a7c27f8b4..dc5128bf1b 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_scan.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_scan.c @@ -240,6 +240,9 @@ static int issue_scan(struct wpa_supplicant *wpa_s, params->scan_time.passive = scan_params->duration; params->scan_time.active.min = scan_params->duration; params->scan_time.active.max = scan_params->duration; + } else { + params->scan_time.active.min = SUPPLICANT_SCAN_ACTIVE_SCAN_MIN_DURATION; + params->scan_time.active.max = SUPPLICANT_SCAN_ACTIVE_SCAN_MAX_DURATION; } } diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_scan_i.h b/components/wpa_supplicant/esp_supplicant/src/esp_scan_i.h index bbea379867..a4b6da0e82 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_scan_i.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_scan_i.h @@ -6,6 +6,10 @@ #ifndef ESP_SCAN_I_H #define ESP_SCAN_I_H + +#define SUPPLICANT_SCAN_ACTIVE_SCAN_MIN_DURATION 10 +#define SUPPLICANT_SCAN_ACTIVE_SCAN_MAX_DURATION 70 + void esp_scan_init(struct wpa_supplicant *wpa_s); void esp_scan_deinit(struct wpa_supplicant *wpa_s); int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender, From b23e29d5ae890184b47421f481e3f4a8fcbfe72c Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Wed, 16 Aug 2023 12:08:02 +0530 Subject: [PATCH 07/12] fix(wpa_supplicant): Add current bss channel as hint to scanning - Add current bss channel as hint to scanning during supplicant connect making the process faster. --- components/wpa_supplicant/esp_supplicant/src/esp_common.c | 1 + components/wpa_supplicant/src/common/wnm_sta.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_common.c b/components/wpa_supplicant/esp_supplicant/src/esp_common.c index e6dac19a54..f84821d699 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_common.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_common.c @@ -612,6 +612,7 @@ void wpa_supplicant_connect(struct wpa_supplicant *wpa_s, /* We only support roaming in same ESS, therefore only bssid setting is needed */ os_memcpy(config->sta.bssid, bss->bssid, ETH_ALEN); config->sta.bssid_set = 1; + config->sta.channel = bss->channel; /* supplicant connect will only be called in case of bss transition(roaming) */ esp_wifi_internal_issue_disconnect(WIFI_REASON_BSS_TRANSITION_DISASSOC); esp_wifi_set_config(WIFI_IF_STA, config); diff --git a/components/wpa_supplicant/src/common/wnm_sta.c b/components/wpa_supplicant/src/common/wnm_sta.c index 4d2c7752e5..9613d8d016 100644 --- a/components/wpa_supplicant/src/common/wnm_sta.c +++ b/components/wpa_supplicant/src/common/wnm_sta.c @@ -805,6 +805,9 @@ static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) MAC2STR(nei->bssid)); return; } + if (wpa_s->current_bss && (os_memcmp(nei->bssid, wpa_s->current_bss->bssid, ETH_ALEN) == 0)) { + continue; + } if (nei->channel_number != chan) { chan = nei->channel_number; num_chan++; From 71e6c10f7c89fa8c8c54c9767119bd8f57d53787 Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Wed, 16 Aug 2023 12:58:01 +0530 Subject: [PATCH 08/12] fix(wpa_supplicant): Update supplicant last scan time when application scanning - Any scanning currently updates the bss table held by supplicant anyway, but the time record used to maintain the recency of the last scan is only updated by supplicant issued scans. Updating the last scan time for application trigerred scanning will prevent the needless scanning by supplicant if there has been an application trigerred scan in the time diff threshold(currently 10s). --- components/wpa_supplicant/esp_supplicant/src/esp_scan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_scan.c b/components/wpa_supplicant/esp_supplicant/src/esp_scan.c index dc5128bf1b..92b9c900ad 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_scan.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_scan.c @@ -162,6 +162,7 @@ int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender, /* update rest of the frame */ os_memcpy(ptr, frame, len); wpa_bss_update_scan_res(wpa_s, res, &now); + os_get_reltime(&wpa_s->last_scan); os_free(res); return 0; From 9a88dab7482876e7e8f5c9494bb974ebcc74ff36 Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Fri, 29 Mar 2024 18:39:47 +0530 Subject: [PATCH 09/12] feat(esp_wifi): Add support for advanced roaming as a wifi app - Adds support for advanced roaming as a wifi app. --- Kconfig | 1 + components/esp_wifi/CMakeLists.txt | 10 +- components/esp_wifi/Kconfig | 20 + .../wifi_apps/{ => nan_app}/include/esp_nan.h | 0 .../wifi_apps/{ => nan_app}/src/nan_app.c | 2 +- .../roaming_app/include/esp_roaming.h | 131 +++ .../wifi_apps/roaming_app/src/Kconfig.roaming | 174 ++++ .../wifi_apps/roaming_app/src/README.md | 92 ++ .../wifi_apps/roaming_app/src/roaming_app.c | 822 ++++++++++++++++++ components/wpa_supplicant/CMakeLists.txt | 1 + .../esp_supplicant/src/esp_common.c | 18 +- .../esp_supplicant/src/esp_wpa_main.c | 7 + docs/doxygen/Doxyfile | 4 +- 13 files changed, 1276 insertions(+), 6 deletions(-) rename components/esp_wifi/wifi_apps/{ => nan_app}/include/esp_nan.h (100%) rename components/esp_wifi/wifi_apps/{ => nan_app}/src/nan_app.c (99%) create mode 100644 components/esp_wifi/wifi_apps/roaming_app/include/esp_roaming.h create mode 100644 components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming create mode 100644 components/esp_wifi/wifi_apps/roaming_app/src/README.md create mode 100644 components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c diff --git a/Kconfig b/Kconfig index 2dba868a97..4330006c89 100644 --- a/Kconfig +++ b/Kconfig @@ -629,3 +629,4 @@ mainmenu "Espressif IoT Development Framework Configuration" - CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH - CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL - CONFIG_ESP_WIFI_EAP_TLS1_3 + - CONFIG_ESP_WIFI_ENABLE_ROAMING_APP diff --git a/components/esp_wifi/CMakeLists.txt b/components/esp_wifi/CMakeLists.txt index 29caded012..5df6dc4b27 100644 --- a/components/esp_wifi/CMakeLists.txt +++ b/components/esp_wifi/CMakeLists.txt @@ -37,7 +37,10 @@ if(CONFIG_ESP_WIFI_ENABLED OR CONFIG_ESP_HOST_WIFI_ENABLED) endif() if(CONFIG_ESP_WIFI_NAN_ENABLE) - list(APPEND srcs "wifi_apps/src/nan_app.c") + list(APPEND srcs "wifi_apps/nan_app/src/nan_app.c") + endif() + if(CONFIG_ESP_WIFI_ENABLE_ROAMING_APP) + list(APPEND srcs "wifi_apps/roaming_app/src/roaming_app.c") endif() set(local_include_dirs include/local) else() @@ -50,10 +53,13 @@ else() endif() idf_component_register(SRCS "${srcs}" - INCLUDE_DIRS "include" "wifi_apps/include" ${local_include_dirs} + INCLUDE_DIRS "include" "wifi_apps/include" "wifi_apps/nan_app/include" ${local_include_dirs} + REQUIRES esp_event esp_phy esp_netif PRIV_REQUIRES driver esptool_py esp_pm esp_timer nvs_flash wpa_supplicant hal lwip esp_coex ${extra_priv_requires} + PRIV_INCLUDE_DIRS ../wpa_supplicant/src/ ../wpa_supplicant/esp_supplicant/src/ + wifi_apps/roaming_app/include LDFRAGMENTS "${ldfragments}") if(CONFIG_ESP_WIFI_ENABLED OR CONFIG_ESP_HOST_WIFI_ENABLED) diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index f9c534a510..72157799a7 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -574,6 +574,26 @@ menu "Wi-Fi" help Select this option to enable WiFi Multiband operation certification support. + config ESP_WIFI_ENABLE_ROAMING_APP + bool "Advanced support for Wi-Fi Roaming (Experimental)" + depends on IDF_EXPERIMENTAL_FEATURES + default n + select ESP_WIFI_SCAN_CACHE + help + Enable Espressif's roaming app to allow for efficient Wi-Fi roaming. + This includes configurable periodic environment scans, maintaining a cache of the + best APs, handling low rssi events etc. + + Risk Warning + - Please note that this feature is still experimental and enabling this potentially can + lead to unpredictable scanning, connection and roaming attempts. + We are still working on tuning and optimising this feature to ensure reliable and stable use. + + menu "Configure roaming App" + depends on ESP_WIFI_ENABLE_ROAMING_APP + rsource "wifi_apps/roaming_app/src/Kconfig.roaming" + endmenu + config ESP_WIFI_DPP_SUPPORT bool "Enable DPP support" default n diff --git a/components/esp_wifi/wifi_apps/include/esp_nan.h b/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h similarity index 100% rename from components/esp_wifi/wifi_apps/include/esp_nan.h rename to components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h diff --git a/components/esp_wifi/wifi_apps/src/nan_app.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c similarity index 99% rename from components/esp_wifi/wifi_apps/src/nan_app.c rename to components/esp_wifi/wifi_apps/nan_app/src/nan_app.c index 8e52bc7de7..240420e036 100644 --- a/components/esp_wifi/wifi_apps/src/nan_app.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c @@ -533,7 +533,7 @@ static void nan_app_action_ndp_confirm(void *arg, esp_event_base_t event_base, i } if (nan_find_ndl(evt->ndp_id, NULL) == NULL) { - /* As ndl isn't found, timeout has occured for NDP response and datapath request is rejected */ + /* As ndl isn't found, timeout has occurred for NDP response and datapath request is rejected */ goto done; } if (evt->status == NDP_STATUS_REJECTED) { diff --git a/components/esp_wifi/wifi_apps/roaming_app/include/esp_roaming.h b/components/esp_wifi/wifi_apps/roaming_app/include/esp_roaming.h new file mode 100644 index 0000000000..7f5e581e08 --- /dev/null +++ b/components/esp_wifi/wifi_apps/roaming_app/include/esp_roaming.h @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_wifi_types.h" +#include "utils/common.h" +#include +#ifdef __cplusplus +extern "C" { +#endif + +#define SUPPLICANT_CANDIDATE_LIST_EXPIRY 10 + +/* Global Roaming Configuration */ +#define ROAMING_BACKOFF_TIME CONFIG_ESP_WIFI_ROAMING_BACKOFF_TIME + +/* Low RSSI based roaming configuration */ +#define LOW_RSSI_ROAMING_ENABLED CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_ROAMING +#if LOW_RSSI_ROAMING_ENABLED +#define ROAMING_LOW_RSSI_THRESHOLD CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_THRESHOLD +#define RSSI_THRESHOLD_REDUCTION_OFFSET CONFIG_ESP_WIFI_ROAMING_LOW_RSSI_OFFSET +#endif /*LOW_RSSI_ROAMING_ENABLED*/ + +/* Periodic Scan based Roaming configuration */ +#define PERIODIC_SCAN_MONITORING CONFIG_ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR +#if PERIODIC_SCAN_MONITORING +#define SCAN_MONITOR_INTERVAL CONFIG_ESP_WIFI_ROAMING_SCAN_MONITOR_INTERVAL +#define SCAN_MONITOR_RSSI_THRESHOLD CONFIG_ESP_WIFI_ROAMING_PERIODIC_SCAN_THRESHOLD +#define SCAN_ROAM_RSSI_DIFF CONFIG_ESP_WIFI_ROAMING_SCAN_ROAM_RSSI_DIFF +#endif /* PERIODIC_SCAN_MONITORING */ + +/* Scan configuration */ +#define SCAN_TIME_MIN_DURATION CONFIG_ESP_WIFI_ROAMING_SCAN_MIN_SCAN_TIME +#define SCAN_TIME_MAX_DURATION CONFIG_ESP_WIFI_ROAMING_SCAN_MAX_SCAN_TIME +#define HOME_CHANNEL_DWELL_TIME CONFIG_ESP_WIFI_ROAMING_HOME_CHANNEL_DWELL_TIME +#define SCAN_PREFERRED_CHAN_LIST CONFIG_ESP_WIFI_ROAMING_SCAN_CHAN_LIST +#define DEFAULT_PREFERRED_SCAN_CHAN_LIST "None" +#define SCAN_RESULTS_USABILITY_WINDOW CONFIG_ESP_WIFI_ROAMING_SCAN_EXPIRY_WINDOW +#define MAX_CANDIDATE_COUNT CONFIG_ESP_WIFI_ROAMING_MAX_CANDIDATES + +/* Legacy roaming configuration */ +#define LEGACY_ROAM_ENABLED CONFIG_ESP_WIFI_ROAMING_LEGACY_ROAMING + +#define BSS_TM_RETRY_COUNT CONFIG_ESP_WIFI_NETWORK_ASSISTED_ROAMING_RETRY_COUNT + +/* Network Assisted Roaming */ +#define NETWORK_ASSISTED_ROAMING_ENABLED CONFIG_ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM + +/* Periodic RRM configuration */ +#define PERIODIC_RRM_MONITORING CONFIG_ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING +#if PERIODIC_RRM_MONITORING +#define RRM_MONITOR_TIME CONFIG_ESP_WIFI_ROAMING_RRM_MONITOR_TIME +#define RRM_MONITOR_RSSI_THRESHOLD CONFIG_ESP_WIFI_ROAMING_RRM_MONITOR_THRESHOLD +#endif /*PERIODIC_RRM_MONITORING*/ + +#define MAX_SCAN_CHAN_LIST_COUNT 14 + +#define MAX_NEIGHBOR_LEN 512 + +#define IS_PSK(authmode) \ + (((authmode == WIFI_AUTH_WPA_PSK) || (authmode == WIFI_AUTH_WPA2_PSK) || \ + (authmode == WIFI_AUTH_WPA_WPA2_PSK) || (authmode == WIFI_AUTH_WPA3_PSK) || \ + (authmode == WIFI_AUTH_WPA2_WPA3_PSK) || (authmode == WIFI_AUTH_WAPI_PSK) ? 1 : 0)) + +#define OWE_COMPATIBLE(curr_auth, cand_auth) \ + ((((curr_auth == WIFI_AUTH_OPEN) || (curr_auth == WIFI_AUTH_OWE)) && ((cand_auth == WIFI_AUTH_OPEN) || (cand_auth == WIFI_AUTH_OWE)))? 1 : 0) + +#define PSK_COMPATIBLE(curr_auth, cand_auth) \ + ((IS_PSK(curr_auth) && IS_PSK(cand_auth)) ? 1 : 0) + +struct scanned_ap_info { + uint16_t current_count; + struct timeval time; + wifi_ap_record_t ap_records[MAX_CANDIDATE_COUNT]; +}; +struct cand_bss { + uint8_t channel; + uint8_t bssid[ETH_ALEN]; +}; + +struct roaming_app { + wifi_scan_config_t scan_params; + bool scan_ongoing; + int8_t current_rssi_threshold; + char *btm_neighbor_list; + struct timeval last_roamed_time; + wifi_ap_record_t ap_info; + struct scanned_ap_info scanned_aps; + bool btm_support; + bool rrm_support; + +#if LOW_RSSI_ROAMING_ENABLED + int8_t current_low_rssi_threshold; +#endif +#if LEGACY_ROAM_ENABLED && NETWORK_ASSISTED_ROAMING_ENABLED + uint8_t btm_attempt; +#endif +#if LEGACY_ROAM_ENABLED + bool force_roam_ongoing; +#endif +#if PERIODIC_RRM_MONITORING + bool periodic_rrm_active; + bool rrm_request_active; +#endif +#if PERIODIC_SCAN_MONITORING + bool periodic_scan_active; +#endif +}; + +void init_roaming_app(void); +void deinit_roaming_app(void); + +#if PERIODIC_RRM_MONITORING +void roaming_app_periodic_rrm_internal_handler(void *data, void *ctx); +#endif /*PERIODIC_RRM_MONITORING*/ + +#if PERIODIC_SCAN_MONITORING +void roaming_app_periodic_scan_internal_handler(void *data, void *ctx); +#endif /*PERIODIC_SCAN_ROAM_MONITORING*/ + +void roaming_app_trigger_roam_internal_handler(void *data, void *ctx); +#ifdef __cplusplus +} +#endif diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming new file mode 100644 index 0000000000..aa79fbdf93 --- /dev/null +++ b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming @@ -0,0 +1,174 @@ +# Visible if ESP_WIFI_ENABLE_ROAMING_APP enabled in components/esp_wifi/Kconfig +menu "Roaming triggers" + config ESP_WIFI_ROAMING_LOW_RSSI_ROAMING + bool "Use Low RSSI to trigger roaming." + default y + help + Enable to use a RSSI threshold to trigger roaming. + + config ESP_WIFI_ROAMING_LOW_RSSI_THRESHOLD + depends on ESP_WIFI_ROAMING_LOW_RSSI_ROAMING + int "WiFi RSSI threshold to trigger roaming" + range -99 -30 + default -60 + help + WiFi RSSI threshold to trigger roaming value in dBm (-99 to -1). Values under -30 dbm + might lead to a flood of low rssi events. This interferes with normal functioning and + TX/Rx performance. + + config ESP_WIFI_ROAMING_LOW_RSSI_OFFSET + depends on ESP_WIFI_ROAMING_LOW_RSSI_ROAMING + int "Offset by which to reset the RSSI Threshold after attempt to roam." + range 0 99 + default 5 + help + Decide the offset by which to decrease the Low RSSI threshold set by ESP_WIFI_ROAMING_LOW_RSSI_THRESHOLD + after each failed attempt to roam. This allows for the station to keep scanning for better AP's after + the Low RSSI threshold is reached in a stepped manner, rather than only attempting to roam the first time + the current AP's RSSI breaches the set RSSI threshold. + + config ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR + bool "Conduct periodic scans to check if a better AP is available" + default y + help + Conduct periodic scans periodically to check if a better AP is available. + + config ESP_WIFI_ROAMING_PERIODIC_SCAN_THRESHOLD + int "Threshold at which to begin periodic scanning for a better AP." + depends on ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR + range -99 -1 + default -50 + help + Threshold at which the station will begin scanning to find an AP with better RSSI. + + config ESP_WIFI_ROAMING_SCAN_MONITOR_INTERVAL + int "Time intervals (in seconds) at which station will initiate a scan" + depends on ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR + range 1 1500 + default 30 + help + Intervals at which station will periodically scan to check if better AP is available + + config ESP_WIFI_ROAMING_SCAN_ROAM_RSSI_DIFF + int "RSSI difference b/w current AP and candidate AP to initiate connection" + depends on ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR + default 15 + range 0 99 + help + Minimum RSSI difference b/w current AP and a potential roaming candidate AP + to trigger a roaming attempt. +endmenu #"Roaming triggers" + +menu "Roaming Methods" + config ESP_WIFI_ROAMING_LEGACY_ROAMING + bool "Support Legacy roaming approach" + default y + help + Roaming between APs that do not support 802.11kv. + This will allow station to roam even when connection is not BTM supported, + by forcefully disconnecting from current AP and connecting to better AP. + + config ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM + bool "Support Network Assisted roaming using 802.11kv" + depends on ESP_WIFI_11KV_SUPPORT + default y + help + Roaming between APs using network assisted Roaming. + This involves BSS Transition Management mechanisms outlined in 802.11v. + Note that this moves the responsibility to the AP's network, and hence isn't + guaranteed to cause the station to attempt to roam each time. + + config ESP_WIFI_NETWORK_ASSISTED_ROAMING_RETRY_COUNT + int "Retry count after which to switch to legacy roaming" + depends on ESP_WIFI_ROAMING_NETWORK_ASSISTED_ROAM + depends on ESP_WIFI_ROAMING_LEGACY_ROAMING + range 1 5 + default 2 + help + Retry threshold after which the station should stop using Network Assisted + roaming methods and start using legacy roaming instead. + +endmenu #"Roaming Methods" + +menu "Scan Configuration" + + config ESP_WIFI_ROAMING_SCAN_MIN_SCAN_TIME + int "Minimum duration (in milliseconds) of station's per channel active scan" + default 10 + range 0 120 + help + Minimum duration of active scanning per channel in milliseconds. + + config ESP_WIFI_ROAMING_SCAN_MAX_SCAN_TIME + int "Maximum duration (in milliseconds) of station's per channel active scan time" + default 70 + range 30 120 + help + Maximum duration of active scanning per channel in milliseconds. + + config ESP_WIFI_ROAMING_HOME_CHANNEL_DWELL_TIME + int "Home channel dwell time scanning between consecutive channels" + default 30 + range 30 150 + help + If connected, duration for which the station will return to it's home channel for Tx/Rx of + frames stored in buffers between scanning on consecutive channels. + + config ESP_WIFI_ROAMING_SCAN_CHAN_LIST + string "Preferred channel list for scanning" + default "None" + help + Channels your wireless network operates on to allow for faster scanning. + Specify the channels(between 1-14) in a comma separated manner. + + config ESP_WIFI_ROAMING_SCAN_EXPIRY_WINDOW + int "Scan results expiry window (in seconds)" + default 10 + range 5 20 + help + Duration for which the results from the most recent scans can be used + by the roaming app for determining the roaming candidates. + + config ESP_WIFI_ROAMING_MAX_CANDIDATES + int "Max Candidates in the network" + default 3 + range 3 20 + help + Max candidates that can be considered while scanning as a part of the + network at one time. + +endmenu #"Scan Configuration" + +config ESP_WIFI_ROAMING_BACKOFF_TIME + int "Default time to wait between subsequent roaming attempts." + default 15 + range 0 120 + help + Time to wait (in seconds) by station before registering for the RSSI event again + or start continuous montoring to find better AP. + +config ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING + bool "Send periodic neighbor report request to AP for internal list updation" + depends on ESP_WIFI_11KV_SUPPORT + default y + help + This option will enable station to keep sending RRM neighbor list request to AP and + update its internal list. + +config ESP_WIFI_ROAMING_RRM_MONITOR_TIME + int "Time interval (in seconds) between neighbor report requests to an AP" + depends on ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING + default 60 + range 0 1500 + help + Enable this to send periodic neighbor report requests to the AP. + These neighbor report requests provide information about other APs in the same managed + network. This information is used for more intelligent roaming. + +config ESP_WIFI_ROAMING_RRM_MONITOR_THRESHOLD + int "Threshold for sending periodic neighbor report requests" + depends on ESP_WIFI_ROAMING_PERIODIC_RRM_MONITORING + default -20 + range -99 0 + help + The RSSI threshold beyond which we start sending periodic neighbor report requests. diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/README.md b/components/esp_wifi/wifi_apps/roaming_app/src/README.md new file mode 100644 index 0000000000..fae12e93a9 --- /dev/null +++ b/components/esp_wifi/wifi_apps/roaming_app/src/README.md @@ -0,0 +1,92 @@ +**Introduction** + +The advanced Wi-Fi roaming app has been written with the intention of simplifying the process of developing applications that will function in a network environment that allows for roaming between the service areas of multiple compatible APs. It gathers basic approaches about how different roaming mechanisms and APIs can be integrated for an efficient solution to roaming and bundles into an easy to user yet highly configurable module. + +**How to use:** + +To enable the roaming app in the menuconfig, please navigate to Component Settings --> Wi-Fi and then enable “Advanced support for Wi-Fi Roaming” + + +**Configuring the advanced Wi-Fi roaming app :** + +After enabling the roaming app in the menuconfig, this roaming app can be configured to best suit your application requirements and the network environment. The configurations are classified into Roaming Triggers (better understood as “Under what conditions to roam?”), Roaming methods (better understood as “How to Roam’), and then some additional configurations such as scanning parameters, backoff times, periodic neighbor report requests etc. + + +**Roaming Triggers: (Roaming Module Settings --> Roaming Triggers)** + +There are broadly two different Roaming triggers you can choose from: + +1) Low RSSI triggered roaming : + +If enabled, in this method the roaming app sets a Wi-Fi Threshold (configured by “Wi-Fi RSSI threshold to trigger roaming”), which when reached by the connection to the current AP, triggers a check for a better AP and if found will trigger roaming. + +Every time the threshold is reached, a new threshold needs to be set at an even lower rssi threshold since if we fail to find a better AP the first time the RSSI threshold is reached there will be no further attempts to find better APs leading to possible disconnection as the only avenue for finding a better AP. The offset by which the next RSSI threshold can be set is decided by “Wi-Fi RSSI threshold to trigger roaming” which defaults to 5. + +Also please note that if the AP we connect to, upon connecting itself has a worse RSSI than the threshold set in the configuration, the new RSSI threshold is set to the current RSSI - offset. Additionally, the RSSI threshold gets reset to the configured value if the RSSI ever goes below the configured threshold by the offset amount. + +2) Periodic Scan based roaming: + +Unlike Low RSSI triggered roaming, which is a bit reactive to the changing network environment, periodic scanning-based roaming (enabled by “Conduct periodic scans to check if a better AP is available”) allows for a more active approach to ensuring that you are connected to the best AP in the network. You can also decide the threshold after which you want to conduct the periodic scans with a default of –20dbm. (Configured by “Threshold at which to begin periodic scanning for a better AP”) + +The intervals at which this periodic scan will take place can also be configured through “Time intervals at which station will initiate a scan”. This defaults to 30 seconds.The RSSI difference between a candidate AP and the current AP, which can be considered as acceptable to initiate roaming can also be configured as the “RSSI difference b/w current AP and a candidate AP to initiate roaming” + +**Please note that at least one of the two Roaming Triggers needs to be enabled.** + + +**Roaming Methods** + +Currently 2 methods of roaming are supported by the roaming app. (Roaming App Settings --> Roaming Methods) + +1) Network Assisted Roaming: + +Enabled by “Support Network Assisted Roaming using 802.11kv”, this method primarily uses the BSS transition Management mechanisms outlined in IEEE 802.11v. It uses Candidates received from neighbor report requests (if enabled, explained later.) and scanning results to Send BSS transition Management Queries to the AP it is currently associated to. Depending on the current radio environment and vendor implementation on the side of the AP, this could then lead to BSS Transition Management Requests and corresponding BSS Transition Management responses which could lead to a seamless transition from one AP to another. For a better understanding of the mechanisms involved and the general implementation please look up the IEEE 802.11v specification and upstream wpa_supplicant’s implementation. + +Please note that for this to work as expected, the APs should support 802.11k & 802.11v and be setup in a network where they are aware of each other. + +2) Legacy Roaming approach. + +Enabled by “Support Legacy roaming approach”, this is designed for scenarios where there is no support for 802.11v amongst the APs of the network. In this scenario, upon determining the better AP to connect to, the station forcibly will disconnect and attempt to connect to the AP with the better AP. + +Please note that if both approaches are enabled, there is an additional configuration (“Retry count after which to switch to legacy roaming”) that allows developers to decide how many times should the station attempt to roam using the Network Assisted roaming methods before switching to using the legacy approach. + +**Please note that at least one of the two roaming methods needs to be selected.** + + +**Scan configuration** + +The scan configuration allows for configuring the parameters for the scans that are conducted in various scenarios during the functioning of the roaming app. Please note that all scans conducted by the roaming app are active scans. + +The minimum and maximum active scanning duration for each channel in milliseconds can be configured through “Minimum duration of active scan time for a station” & “Maximum duration of active scan time for a station”. If connected, the “Home channel dwell time between scanning consecutive channels” configuration decides for how long the station will return to the home channel for Tx of various frames in the buffer, and Rx of frames buffered by the AP. + +Additionally, if channels of operation of the APs that the application designed will work with is known, you can provide this information as an ordered list of comma-separated channels. (configured using “Preferred channel list for scanning”) (for e.g. 1,6,9,11) Only those channels will be scanned in the order mentioned. Keeping in mind that network discovery/scanning is the process that takes up most of the time in roaming as well connecting to an AP, providing such a list could significantly increase the efficiency of the roaming app and process. + +This module can trigger scans due to several reasons. The configuration “Scan results expiry window” decides the duration for which different modules will use the most recent scan results instead of triggering a new scan of their own. + + +**Backoff Time:** + +Successive roaming attempts by multiple roaming triggers simultaneously could lead to performance degradation, hence a backoff time can be configured (“Default time to wait between subsequent roaming attempts”). A roaming attempt can be made using any method only once the configured backoff time has passed since the last roaming attempt. + + +**Periodic Neighbor Report Requests:** + +Neighbor Report requests are a part of the IEEE 802.11k specification, and hence the intended network would need to support and be setup in a way to support its mechanisms. Periodic neighbor report requests provide vital information about the network and other APs in the vicinity that are candidate APs of the same network. This can be enabled using “Send Periodic Neighbor Report requests for updating the internal list”. he frequency of these requests is controlled by “Time interval between Periodic Neighbor report Requests”. There can also be a RSSI threshold (“Threshold for sending periodic neighbor report requests”) after which you wish to consider sending these requests however this is set by default to 0. + + +**Notes :** + +1) Advanced roaming support is disabled by default. + +2) When enabling the advanced roaming support , it is expected that the bssid to connect to is not specifically set by the application. This would defeat the purpose of roaming between different APs of the network. Hence if the BSSID is set (in wifi_config_t.sta) , it will be unset at the first disconnection/connection. + +3) For roaming to work as expected, the APs between which the station is expected to roam must have the same or compatible authmode. These include : + + Open <--> OWE + + PSK based authmodes <--> PSK based authmodes (Does not include WEP) + + Enterprise <--> Enterprise. + + Also note that this module would also prevent roaming to an AP that does not clear the authmode threshold set by wifi_config_t.sta. + +4) As a general guideline for configuring the values of the roaming app it would be helpful to understand the trade-off between roaming and general performance of the overall application. Roaming includes processes such as scanning, disconnecting, connecting, etc which take up time away from other tasks of application. Deciding the several configurations in this module needs to be done while prioritising between being connected to the best AP, seamless transfer between APs and performance based on the requirements of the end application. For example, setting the thresholds of processes outlined above to a lower value or reducing the intervals between the aforementioned periodic tasks would make the roaming more aggressive while interrupting the general operation of the whole application more frequently. diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c new file mode 100644 index 0000000000..271e0d04c0 --- /dev/null +++ b/components/esp_wifi/wifi_apps/roaming_app/src/roaming_app.c @@ -0,0 +1,822 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_wifi.h" +#include "esp_wnm.h" +#include "esp_rrm.h" +#include "esp_mbo.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_mac.h" +#include "nvs_flash.h" +#include +#include "regex.h" +#include +#include "esp_roaming.h" +#include "utils/common.h" +#include "esp_wifi_driver.h" +#include "utils/eloop.h" +#include "rom/ets_sys.h" +#include "common/ieee802_11_defs.h" +static struct roaming_app g_roaming_app; + +typedef void (* scan_done_cb_t)(void *arg, ETS_STATUS status); +extern int esp_wifi_promiscuous_scan_start(wifi_scan_config_t *config, scan_done_cb_t cb); +static void *scan_results_lock = NULL; +#define ROAM_SCAN_RESULTS_LOCK() os_mutex_lock(scan_results_lock) +#define ROAM_SCAN_RESULTS_UNLOCK() os_mutex_unlock(scan_results_lock) + +static void *neighbor_list_lock = NULL; +#define ROAM_NEIGHBOR_LIST_LOCK() os_mutex_lock(neighbor_list_lock) +#define ROAM_NEIGHBOR_LIST_UNLOCK() os_mutex_unlock(neighbor_list_lock) + +static int wifi_post_roam_event(struct cand_bss *bss); +static void determine_best_ap(int8_t rssi_threshold); + +static const char *ROAMING_TAG = "ROAM"; + +static inline long time_diff_sec(struct timeval *a, struct timeval *b) +{ + return (a->tv_sec - b->tv_sec); +} +static void roaming_app_get_ap_info(wifi_ap_record_t *ap_info) +{ + esp_wifi_sta_get_ap_info(ap_info); +#if LOW_RSSI_ROAMING_ENABLED + /* + * If the current rssi is below the configured rssi threshold for + * low rssi based roaming and the current rssi threshold is below that, + * we should reset the rssi threshold back to the configured rssi threshold */ + if ((ap_info->rssi > ROAMING_LOW_RSSI_THRESHOLD) && (g_roaming_app.current_low_rssi_threshold < ROAMING_LOW_RSSI_THRESHOLD)) { + g_roaming_app.current_low_rssi_threshold = ROAMING_LOW_RSSI_THRESHOLD; + esp_wifi_set_rssi_threshold(ROAMING_LOW_RSSI_THRESHOLD); + ESP_LOGD(ROAMING_TAG, "Reset the low rssi threshold back to %d", ROAMING_LOW_RSSI_THRESHOLD); + } +#endif /*LOW_RSSI_ROAMING_ENABLED*/ +} +#if LEGACY_ROAM_ENABLED +static void legacy_roam_clear_bssid_flag(void) +{ + wifi_config_t *config = {0}; + + config = os_zalloc(sizeof(wifi_config_t)); + if (!config) { + ESP_LOGE(ROAMING_TAG, "failed to allocate memory"); + return; + } + + esp_wifi_get_config(WIFI_IF_STA, config); + if (config->sta.bssid_set) { + config->sta.bssid_set = 0; + esp_wifi_set_config(WIFI_IF_STA, config); + } + os_free(config); + ESP_LOGD(ROAMING_TAG, "cleared bssid flag"); +} +#endif /*LEGACY_ROAM_ENABLED*/ + +static int8_t initialize_roaming_event(void) +{ +#if LEGACY_ROAM_ENABLED + /* + * Resetting the Bssid param as it is possible that a previous force + * roam has set config to connect to a specific bssid and now further + * roaming attempts using BTM could lead to a spiral of connecting to + * the previous AP */ + if (g_roaming_app.force_roam_ongoing) { + legacy_roam_clear_bssid_flag(); + } +#endif /*LEGACY_ROAM_ENABLED*/ + return ESP_OK; +} + +#if PERIODIC_RRM_MONITORING +static void init_periodic_rrm_event(void) +{ + if (!neighbor_list_lock) { + neighbor_list_lock = os_recursive_mutex_create(); + if (!neighbor_list_lock) { + ESP_LOGE(ROAMING_TAG, "%s: failed to create roaming neighbor list lock", __func__); + } + } + ESP_LOGV(ROAMING_TAG, "Initialised Periodic RRM Monitoring event!"); + g_roaming_app.periodic_rrm_active = true; + if (eloop_register_timeout(RRM_MONITOR_TIME, 0, roaming_app_periodic_rrm_internal_handler, NULL, NULL)) { + ESP_LOGE(ROAMING_TAG, "Could not register periodic neighbor report event."); + } +} +#endif /*PERIODIC_RRM_MONITORING*/ + +#if PERIODIC_SCAN_MONITORING +static void init_periodic_scan_roam_event(void) +{ + ESP_LOGV(ROAMING_TAG, "Initialised Periodic Scan Roam event!"); + g_roaming_app.periodic_scan_active = true; + if (eloop_register_timeout(SCAN_MONITOR_INTERVAL, 0, roaming_app_periodic_scan_internal_handler, NULL, NULL)) { + ESP_LOGE(ROAMING_TAG, "Could not register periodic scan monitoring event"); + } +} +#endif /*PERIODIC_SCAN_ROAM_MONITORING*/ + +static void roaming_app_disconnected_event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + +#if PERIODIC_RRM_MONITORING + g_roaming_app.periodic_rrm_active = false; +#endif /*PERIODIC_RRM_MONITORING*/ + +#if PERIODIC_SCAN_MONITORING + g_roaming_app.periodic_scan_active = false; +#endif /*PERIODIC_SCAN_MONITORING*/ + + wifi_event_sta_disconnected_t *disconn = event_data; + ESP_LOGD(ROAMING_TAG, "station got disconnected reason=%d", disconn->reason); + if (disconn->reason == WIFI_REASON_ROAMING) { + ESP_LOGD(ROAMING_TAG, "station roaming, do nothing"); + } else { +#if LEGACY_ROAM_ENABLED + /* + * Resetting the Bssid param as it is possible that a previous force + * roam has set config to connect to a specific bssid and now further + * roaming attempts using BTM could lead to a spiral of connecting to + * the previous AP */ + if (g_roaming_app.force_roam_ongoing) { + legacy_roam_clear_bssid_flag(); + } +#endif /*LEGACY_ROAM_ENABLED*/ + esp_wifi_connect(); + } +} + +static void roaming_app_connected_event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ +#if LOW_RSSI_ROAMING_ENABLED + roaming_app_get_ap_info(&g_roaming_app.ap_info); + g_roaming_app.scan_params.ssid = g_roaming_app.ap_info.ssid; + if (g_roaming_app.ap_info.rssi < ROAMING_LOW_RSSI_THRESHOLD) { + /* To ensure that the threshold is set to one offset below the current AP RSSI + * in case, the AP is already below the RSSI threshold */ + g_roaming_app.current_low_rssi_threshold = g_roaming_app.ap_info.rssi - RSSI_THRESHOLD_REDUCTION_OFFSET; + } else { + g_roaming_app.current_low_rssi_threshold = ROAMING_LOW_RSSI_THRESHOLD; + } + ESP_LOGD(ROAMING_TAG, "setting rssi threshold as %d", g_roaming_app.current_low_rssi_threshold); + esp_wifi_set_rssi_threshold(g_roaming_app.current_low_rssi_threshold); +#endif /*LOW_RSSI_ROAMING_ENABLED*/ + g_roaming_app.rrm_support = esp_rrm_is_rrm_supported_connection(); + g_roaming_app.btm_support = esp_wnm_is_btm_supported_connection(); + ESP_LOGD(ROAMING_TAG, "Station connected, RRM %ssupported, BTM %ssupported", + g_roaming_app.rrm_support ? " " : "not ", + g_roaming_app.btm_support ? " " : "not "); + gettimeofday(&g_roaming_app.last_roamed_time, NULL); + if (!initialize_roaming_event()) { +#if PERIODIC_RRM_MONITORING + if (g_roaming_app.rrm_support) { + init_periodic_rrm_event(); + } +#endif /*PERIODIC_RRM_MONITORING*/ +#if PERIODIC_SCAN_MONITORING + init_periodic_scan_roam_event(); +#endif /*PERIODIC_SCAN_ROAM_MONITORING*/ + ESP_LOGD(ROAMING_TAG, "Initialised initialise roaming events!"); + } else { + ESP_LOGE(ROAMING_TAG, "Failed to Initialise roaming events"); + } +#if LEGACY_ROAM_ENABLED + g_roaming_app.force_roam_ongoing = true; +#endif /*LEGACY_ROAM_ENABLED*/ +} +#define MAX_NEIGHBOR_LEN 512 +#if PERIODIC_RRM_MONITORING +static char * get_btm_neighbor_list(uint8_t *report, size_t report_len) +{ + size_t len = 0; + const uint8_t *data; + int ret = 0; + + /* + * Neighbor Report element (IEEE P802.11-REVmc/D5.0) + * BSSID[6] + * BSSID Information[4] + * Operating Class[1] + * Channel Number[1] + * PHY Type[1] + * Optional Subelements[variable] + */ +#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1) + + if (!report || report_len == 0) { + ESP_LOGE(ROAMING_TAG, "RRM neighbor report is not valid"); + return NULL; + } + + char *buf = os_calloc(1, MAX_NEIGHBOR_LEN); + if (!buf) { + ESP_LOGE(ROAMING_TAG, "get_btm_list : Memory alloc failed for buf"); + return NULL; + } + data = report; + while (report_len >= 2 + NR_IE_MIN_LEN) { + const uint8_t *nr; + uint8_t nr_len = data[1]; + const uint8_t *pos = data, *end; + + if (pos[0] != WLAN_EID_NEIGHBOR_REPORT || + nr_len < NR_IE_MIN_LEN) { + ESP_LOGD(ROAMING_TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%u", + data[0], nr_len); + ret = -1; + goto cleanup; + } + + if (2U + nr_len > report_len) { + ESP_LOGD(ROAMING_TAG, "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u", + data[0], report_len, nr_len); + ret = -1; + goto cleanup; + } + pos += 2; + end = pos + nr_len; + + nr = pos; + pos += NR_IE_MIN_LEN; + + while (end - pos > 2) { + uint8_t s_len; + s_len = *pos++; + if (s_len > end - pos) { + ret = -1; + goto cleanup; + } + pos += s_len; + } + ESP_LOGD(ROAMING_TAG, "RMM neighbor report bssid=" MACSTR + " info=0x%" PRIx32 " op_class=%u chan=%u phy_type=%u", + MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN), + nr[ETH_ALEN + 4], nr[ETH_ALEN + 5], + nr[ETH_ALEN + 6]); + /* neighbor start */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, " neighbor="); + /* bssid */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, MACSTR, MAC2STR(nr)); + /* , */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); + /* bssid info */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "0x%04" PRIx32 "", WPA_GET_LE32(nr + ETH_ALEN)); + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); + /* operating class */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 4]); + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); + /* channel number */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 5]); + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); + /* phy type */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 6]); + /* optional elements, skip */ + + data = end; + report_len -= 2 + nr_len; + } + +cleanup: + if (ret < 0) { + os_free(buf); + buf = NULL; + } + return buf; +} +static void roaming_app_neighbor_report_recv_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + if (!g_roaming_app.rrm_request_active) { + ESP_LOGV(ROAMING_TAG, "Not the response for our Neighbor Report Request"); + return; + } + g_roaming_app.rrm_request_active = false; + + if (!event_data) { + ESP_LOGE(ROAMING_TAG, "No data received for neighbor report"); + return; + } + + wifi_event_neighbor_report_t *neighbor_report_event = (wifi_event_neighbor_report_t*)event_data; + ESP_LOGD(ROAMING_TAG, "Received cb for Neighbor Report Request"); + + uint8_t *pos = (uint8_t *)neighbor_report_event->report; + if (!pos) { + ESP_LOGE(ROAMING_TAG, "Neighbor report is empty"); + return; + } + + uint8_t report_len = neighbor_report_event->report_len; + /* dump report info */ + ESP_LOGD(ROAMING_TAG, "rrm: neighbor report len=%d", report_len); + ESP_LOG_BUFFER_HEXDUMP(ROAMING_TAG, pos, report_len, ESP_LOG_DEBUG); + + ROAM_NEIGHBOR_LIST_LOCK(); + if (g_roaming_app.btm_neighbor_list) { + os_free(g_roaming_app.btm_neighbor_list); + g_roaming_app.btm_neighbor_list = NULL; + } + /* create neighbor list */ + g_roaming_app.btm_neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1); + ROAM_NEIGHBOR_LIST_UNLOCK(); + +} +#endif /*PERIODIC_RRM_MONITORING*/ +#if LOW_RSSI_ROAMING_ENABLED +static void roaming_app_rssi_low_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + wifi_event_bss_rssi_low_t *event = event_data; + ESP_LOGI(ROAMING_TAG, "%s:bss rssi is=%ld", __func__, event->rssi); + + roaming_app_get_ap_info(&g_roaming_app.ap_info); + determine_best_ap(0); + g_roaming_app.current_low_rssi_threshold -= RSSI_THRESHOLD_REDUCTION_OFFSET; + ESP_LOGD(ROAMING_TAG, "Resetting RSSI Threshold to %d", g_roaming_app.current_low_rssi_threshold); + esp_wifi_set_rssi_threshold(g_roaming_app.current_low_rssi_threshold); + +} +#endif + +#if NETWORK_ASSISTED_ROAMING_ENABLED +static void trigger_network_assisted_roam(void) +{ +#if PERIODIC_RRM_MONITORING + ROAM_NEIGHBOR_LIST_LOCK(); +#endif /*PERIODIC_RRM_MONITORING*/ + if (esp_wnm_send_bss_transition_mgmt_query(REASON_RSSI, g_roaming_app.btm_neighbor_list, 1) < 0) { + ESP_LOGD(ROAMING_TAG, "failed to send btm query"); + } +#if PERIODIC_RRM_MONITORING + ROAM_NEIGHBOR_LIST_UNLOCK(); +#endif /*PERIODIC_RRM_MONITORING*/ + ESP_LOGD(ROAMING_TAG, "Sent BTM Query"); + gettimeofday(&g_roaming_app.last_roamed_time, NULL); + g_roaming_app.btm_attempt++; + +} +#endif /*NETWORK_ASSISTED_ROAMING*/ + +#if LEGACY_ROAM_ENABLED +static void trigger_legacy_roam(struct cand_bss *bss) +{ + wifi_config_t wifi_cfg = {0}; + esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg); + wifi_cfg.sta.channel = bss->channel; + wifi_cfg.sta.bssid_set = true; + os_memcpy(wifi_cfg.sta.bssid, bss->bssid, ETH_ALEN); + esp_wifi_internal_issue_disconnect(WIFI_REASON_BSS_TRANSITION_DISASSOC); + esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg); + esp_wifi_connect(); + ESP_LOGI(ROAMING_TAG, "Disconnecting and connecting to "MACSTR" on account of better rssi",MAC2STR(bss->bssid)); + gettimeofday(&g_roaming_app.last_roamed_time, NULL); + g_roaming_app.force_roam_ongoing = true; +} +#endif /*LEGACY_ROAM_ENABLED*/ + +void roaming_app_trigger_roam(struct cand_bss *bss) +{ + struct timeval now; + gettimeofday(&now, NULL); + ESP_LOGD(ROAMING_TAG,"Processing trigger roaming request."); + if (time_diff_sec(&now, &g_roaming_app.last_roamed_time) < ROAMING_BACKOFF_TIME ) { + ESP_LOGD(ROAMING_TAG,"Ignoring request as time difference to last request is %ld",time_diff_sec(&now, &g_roaming_app.last_roamed_time)); + goto free_bss; + } +#if NETWORK_ASSISTED_ROAMING_ENABLED + if (g_roaming_app.btm_support) { +#if LEGACY_ROAM_ENABLED && NETWORK_ASSISTED_ROAMING_ENABLED + if (g_roaming_app.btm_attempt <= BSS_TM_RETRY_COUNT) { +#endif + trigger_network_assisted_roam(); + goto free_bss; +#if LEGACY_ROAM_ENABLED && NETWORK_ASSISTED_ROAMING_ENABLED + } else { + ESP_LOGD(ROAMING_TAG, "Not Sending BTM query as this method has failed too many times."); + g_roaming_app.btm_attempt = 0; + } +#endif + } +#endif /*NETWORK_ASSISTED_ROAMING_ENABLED*/ +#if LEGACY_ROAM_ENABLED + trigger_legacy_roam(bss); +#endif /*LEGACY_ROAM_ENABLED*/ +free_bss : + os_free(bss); +} + +void roaming_app_trigger_roam_internal_handler(void *ctx, void *data) +{ + if (!data) { + ESP_LOGE(ROAMING_TAG, "No data received for roaming event"); + } else { + roaming_app_trigger_roam((struct cand_bss *)data); + } + +} + +static int wifi_post_roam_event(struct cand_bss *bss) +{ + if (bss) { + struct cand_bss *cand_bss = (struct cand_bss *)os_zalloc(sizeof(struct cand_bss)); + if (!cand_bss) { + ESP_LOGE(ROAMING_TAG, "Cannot allocate data for roaming event"); + return -1; + } + os_memcpy(cand_bss, bss, sizeof(struct cand_bss)); + /* trigger the roaming event */ + if (eloop_register_timeout(0, 0, roaming_app_trigger_roam_internal_handler, NULL, (void*)cand_bss)) { + ESP_LOGE(ROAMING_TAG, "Could not register roaming event."); + os_free(cand_bss); + return -1; + } + } else { + ESP_LOGE(ROAMING_TAG, "Cannot trigger roaming event without any candidate APs"); + return -1; + } + return 0; + +} + +void print_ap_records(struct scanned_ap_info *ap_info) +{ + ESP_LOGD(ROAMING_TAG, "Scanned AP List"); + for (int i = 0; i < ap_info->current_count ; i++) { + ESP_LOGD(ROAMING_TAG, "%d. ssid : %s bssid :"MACSTR" channel : %d rssi : %d authmode : %d", i, + ap_info->ap_records[i].ssid,MAC2STR(ap_info->ap_records[i].bssid), + ap_info->ap_records[i].primary,ap_info->ap_records[i].rssi, ap_info->ap_records[i].authmode); + } + +} + + +#if PERIODIC_RRM_MONITORING +static void periodic_rrm_request(struct timeval *now) +{ + roaming_app_get_ap_info(&g_roaming_app.ap_info); + if (esp_rrm_is_rrm_supported_connection() && (g_roaming_app.ap_info.rssi < RRM_MONITOR_RSSI_THRESHOLD)) { + if (esp_rrm_send_neighbor_report_request() < 0) { + ESP_LOGE(ROAMING_TAG, "failed to send neighbor report request"); + return; + } + g_roaming_app.rrm_request_active = true; + } +} +#else +static void periodic_rrm_request(struct timeval *now) { } +#endif + +static bool candidate_security_match(wifi_ap_record_t candidate) +{ + wifi_auth_mode_t curr_auth = g_roaming_app.ap_info.authmode; + wifi_auth_mode_t cand_auth = candidate.authmode; + ESP_LOGV(ROAMING_TAG, "Cand authmode : %d, Current Authmode : %d", cand_auth, curr_auth); + if (cand_auth == curr_auth) { + ESP_LOGV(ROAMING_TAG, "Authmode matched!"); + return true; + } + wifi_config_t wifi_cfg = {0}; + esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg); + if (wifi_cfg.sta.owe_enabled && OWE_COMPATIBLE(curr_auth, cand_auth)) { + /* + * OWE <--> Open allowed if threshold is Open + */ + if (wifi_cfg.sta.threshold.authmode == WIFI_AUTH_OPEN) { + ESP_LOGV(ROAMING_TAG, "transition between OWE and open permitted"); + return true; + } else { + ESP_LOGV(ROAMING_TAG, "transition between OWE and open not permitted"); + return false; + } + } else if (wifi_cfg.sta.threshold.authmode > cand_auth) { + /* If the authmode of the candidate AP is less than our threshold, it + * will fail during connection*/ + ESP_LOGV(ROAMING_TAG, "Authmode threshold failure %d -> %d", wifi_cfg.sta.threshold.authmode, cand_auth); + return false; + } else if (PSK_COMPATIBLE(curr_auth, cand_auth)) { + /* + * PSK based authmodes are compatible with each other for roaming + */ + ESP_LOGV(ROAMING_TAG, "Roaming between a PSK APs"); + return true; + } + return false; +} + +static bool candidate_profile_match(wifi_ap_record_t candidate) +{ + return candidate_security_match(candidate); +} +/* Remember to always call this function with the ROAM_SCAN_RESULTS_LOCK */ +static void parse_scan_results_and_roam(void) +{ + int8_t rssi_threshold = g_roaming_app.current_rssi_threshold; + uint8_t best_rssi_diff = rssi_threshold; + struct cand_bss *best_ap = NULL; + int8_t rssi_diff = 0; + uint8_t i; + int8_t best_ap_index = -1; + wifi_ap_record_t ap_info; + roaming_app_get_ap_info(&ap_info); + for (i = 0; i < g_roaming_app.scanned_aps.current_count; i++) { + rssi_diff = g_roaming_app.scanned_aps.ap_records[i].rssi - ap_info.rssi; + ESP_LOGD(ROAMING_TAG, "The difference between ("MACSTR", "MACSTR") with rssi (%d,%d) is : %d while the threshold is %d and the best rssi diff yet is %d, thecand_auth is %d", + MAC2STR(g_roaming_app.scanned_aps.ap_records[i].bssid),MAC2STR(ap_info.bssid), + g_roaming_app.scanned_aps.ap_records[i].rssi, ap_info.rssi, + rssi_diff, rssi_threshold, best_rssi_diff, g_roaming_app.scanned_aps.ap_records[i].authmode); + if ((memcmp(g_roaming_app.scanned_aps.ap_records[i].bssid, ap_info.bssid, ETH_ALEN) != 0) && + candidate_profile_match(g_roaming_app.scanned_aps.ap_records[i]) && rssi_diff > best_rssi_diff ) { + best_rssi_diff = rssi_diff; + best_ap_index = i; + } + } + if (best_ap_index >= 0) { + best_ap = (struct cand_bss*)os_zalloc(sizeof(struct cand_bss)); + if (!best_ap) { + ESP_LOGE(ROAMING_TAG,"Memory Allocation for candidate bss failed"); + return; + } + os_memcpy(best_ap->bssid,g_roaming_app.scanned_aps.ap_records[best_ap_index].bssid , ETH_ALEN); + best_ap->channel = g_roaming_app.scanned_aps.ap_records[best_ap_index].primary; + } + + if (best_ap) { + ESP_LOGI(ROAMING_TAG,"Found a better AP "MACSTR" at channel %d", MAC2STR(best_ap->bssid), best_ap->channel); + if (wifi_post_roam_event(best_ap)) { + ESP_LOGE(ROAMING_TAG, "Posting of roaming event failed"); + } + os_free(best_ap); + } else { + ESP_LOGI(ROAMING_TAG, "Could not find a better AP with the threshold set to %d", g_roaming_app.current_rssi_threshold + 1); + } +} + +static void scan_done_event_handler(void *arg, ETS_STATUS status) +{ + if (status == ETS_OK) { + ROAM_SCAN_RESULTS_LOCK(); + ESP_LOGD(ROAMING_TAG, "Scan Done properly"); + g_roaming_app.scanned_aps.current_count = MAX_CANDIDATE_COUNT; + esp_wifi_scan_get_ap_records(&g_roaming_app.scanned_aps.current_count, g_roaming_app.scanned_aps.ap_records); + print_ap_records(&g_roaming_app.scanned_aps); + parse_scan_results_and_roam(); + g_roaming_app.scan_ongoing = false; + ROAM_SCAN_RESULTS_UNLOCK(); + } else { + ESP_LOGD(ROAMING_TAG, "Scan Done with error %d ", status); + } +} +static void conduct_scan(void) +{ + /* Update scan time in global structure */ + gettimeofday(&g_roaming_app.scanned_aps.time, NULL); + /* Issue scan */ + os_memset(&g_roaming_app.scanned_aps, 0, sizeof(struct scanned_ap_info)); + if (esp_wifi_promiscuous_scan_start(&g_roaming_app.scan_params, scan_done_event_handler) < 0) { + ESP_LOGE(ROAMING_TAG, "failed to issue scan"); + return; + } + ESP_LOGI(ROAMING_TAG, "Issued Scan"); +} + +static void determine_best_ap(int8_t rssi_threshold) +{ + struct timeval now; + gettimeofday(&now, NULL); + ROAM_SCAN_RESULTS_LOCK(); + /* If the scan results are recent enough or a scan is already ongoing we should not trigger a new scan */ + if (!g_roaming_app.scan_ongoing) { + g_roaming_app.scan_ongoing = true; + g_roaming_app.current_rssi_threshold = rssi_threshold; + if (time_diff_sec(&now,&g_roaming_app.scanned_aps.time) > SCAN_RESULTS_USABILITY_WINDOW) { + conduct_scan(); + } else { + parse_scan_results_and_roam(); + g_roaming_app.scan_ongoing = false; + } + } else if(rssi_threshold < g_roaming_app.current_rssi_threshold) { + g_roaming_app.current_rssi_threshold = rssi_threshold; + } + ROAM_SCAN_RESULTS_UNLOCK(); +} +#if PERIODIC_SCAN_MONITORING +static void periodic_scan_roam(struct timeval *now) +{ +#if NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED + /* + * In case we support only network assisted roaming, there is no need to scan + * if we currently have over 10secs to go for backoff time to expire + * as the results produced by a scan at this time would not be used by + * supplicant to build candidate lists. + * */ + if (time_diff_sec(now, &g_roaming_app.last_roamed_time) < ROAMING_BACKOFF_TIME - SUPPLICANT_CANDIDATE_LIST_EXPIRY) { + return; + } +#endif /*NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED*/ + /* If the current RSSI is not worse than the configured threshold + * for station initiated roam, then do not trigger roam */ + roaming_app_get_ap_info(&g_roaming_app.ap_info); + if (g_roaming_app.ap_info.rssi > SCAN_MONITOR_RSSI_THRESHOLD) { + return; + } + + determine_best_ap(SCAN_ROAM_RSSI_DIFF - 1); +} +#endif /*PERIODIC_SCAN_MONITORING*/ + +#if PERIODIC_RRM_MONITORING +void roaming_app_periodic_rrm_internal_handler(void *data, void *ctx) +{ + struct timeval now; + if (g_roaming_app.periodic_rrm_active) { + ESP_LOGD(ROAMING_TAG,"Triggered periodic rrm event!"); + gettimeofday(&now, NULL); + /* This will be done every RRM_MONITOR_TIME */ + periodic_rrm_request(&now); + + if (eloop_register_timeout(RRM_MONITOR_TIME, 0, roaming_app_periodic_rrm_internal_handler, NULL, NULL)) { + ESP_LOGE(ROAMING_TAG,"Could not register periodic neighbor report request event."); + } + } +} +#endif /*PERIODIC_RRM_MONITORING*/ + +#if PERIODIC_SCAN_MONITORING +void roaming_app_periodic_scan_internal_handler(void *data, void *ctx) +{ + struct timeval now; + if (g_roaming_app.periodic_scan_active) { + ESP_LOGD(ROAMING_TAG,"Started the periodic scan roam event!"); + gettimeofday(&now, NULL); + + /* This will be done every SCAN_MONITOR_INTERVAL */ + periodic_scan_roam(&now); + + if (eloop_register_timeout(SCAN_MONITOR_INTERVAL, 0, roaming_app_periodic_scan_internal_handler, NULL, NULL)) { + ESP_LOGE(ROAMING_TAG,"Could not register periodic scan event."); + } + } +} +#endif /*PERIODIC_SCAN_ROAM_MONITORING*/ +static bool validate_scan_chan_list(const char* scan_chan_list) +{ + regex_t regex; + const char* pattern = "^[0-9]+(,[0-9]+)*$"; + uint8_t ret = regcomp(®ex, pattern, REG_EXTENDED); + if (ret != 0) { + ESP_LOGE(ROAMING_TAG, "Regex compilation failed."); + return false; + } + + ret = regexec(®ex, scan_chan_list, 0, NULL, 0); + regfree(®ex); + + if (ret == REG_NOMATCH) { + return false; + } + + /* Check for consecutive commas or comma at start/end */ + if (strstr(scan_chan_list, ",,") != NULL || scan_chan_list[0] == ',' || scan_chan_list[strlen(scan_chan_list) - 1] == ',') { + return false; + } + + return true; +} + +static int8_t parse_scan_chan_list(void) +{ + int8_t ret = 0; + char *scan_chan_string = NULL; + if (validate_scan_chan_list(SCAN_PREFERRED_CHAN_LIST) == false) { + ESP_LOGE(ROAMING_TAG, "scan chan list validation failed."); + ret = -1; + goto cleanup; + } + scan_chan_string = (char *)os_zalloc(sizeof(char) * strlen(SCAN_PREFERRED_CHAN_LIST) + 1); + if (scan_chan_string == NULL) { + ESP_LOGE(ROAMING_TAG, "Memory allocation failed."); + ret = -1; + goto cleanup; + } + strlcpy(scan_chan_string, SCAN_PREFERRED_CHAN_LIST, strlen(SCAN_PREFERRED_CHAN_LIST) + 1); + char* token; + token = strsep(&scan_chan_string, ","); + + g_roaming_app.scan_params.channel_bitmap.ghz_2_channels = 0; + + while (token != NULL) { + uint8_t channel = atoi(token); + /* Check if the number is within the required range */ + if (channel >= 1 && channel <= 14) { + /* Check if the number is already present in the array */ + g_roaming_app.scan_params.channel_bitmap.ghz_2_channels |= (1 << channel); + } else { + ESP_LOGE(ROAMING_TAG, "Channel out of range: %d", channel); + ret = -1; + goto cleanup; + } + token = strsep(&scan_chan_string, ","); + } + +cleanup: + if (scan_chan_string) { + os_free(scan_chan_string); + } + return ret; +} + +esp_err_t init_scan_params(void) +{ + if (!scan_results_lock) { + scan_results_lock = os_recursive_mutex_create(); + if (!scan_results_lock) { + ESP_LOGE(ROAMING_TAG, "%s: failed to create scan results lock", __func__); + return ESP_FAIL; + } + } + + if (strcmp(DEFAULT_PREFERRED_SCAN_CHAN_LIST, SCAN_PREFERRED_CHAN_LIST)) { + ESP_ERROR_CHECK(parse_scan_chan_list()); + } + + g_roaming_app.scan_params.scan_type = 0; + g_roaming_app.scan_params.scan_time.active.min = SCAN_TIME_MIN_DURATION; + g_roaming_app.scan_params.scan_time.active.max = SCAN_TIME_MAX_DURATION; + g_roaming_app.scan_params.home_chan_dwell_time = HOME_CHANNEL_DWELL_TIME; + gettimeofday(&g_roaming_app.scanned_aps.time, NULL); + return ESP_OK; +} + +void init_roaming_app(void) +{ +#if !LOW_RSSI_ROAMING_ENABLED && !PERIODIC_SCAN_MONITORING + ESP_LOGE(ROAMING_TAG, "No roaming method enabled. Roaming app cannot be initialized"); + return; +#endif + +#if !NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED + ESP_LOGE(ROAMING_TAG, "No roaming method enabled. Roaming app cannot be initialized"); + return; +#endif + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, roaming_app_connected_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, roaming_app_disconnected_event_handler, NULL)); +#if LOW_RSSI_ROAMING_ENABLED + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, + &roaming_app_rssi_low_handler, NULL)); +#endif /*ROAMING_LOW_RSSI_THRESHOLD*/ + ESP_ERROR_CHECK(init_scan_params()); +#if PERIODIC_RRM_MONITORING + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, + &roaming_app_neighbor_report_recv_handler, NULL)); +#endif /*PERIODIC_RRM_MONITORING*/ + ESP_LOGI(ROAMING_TAG, "Roaming app initialization done"); +} + +void deinit_roaming_app(void) +{ +#if !LOW_RSSI_ROAMING_ENABLED && !PERIODIC_SCAN_MONITORING + ESP_LOGE(ROAMING_TAG, "No roaming trigger enabled. Roaming app cannot be de-initialized"); + return; +#endif + +#if !NETWORK_ASSISTED_ROAMING_ENABLED && !LEGACY_ROAM_ENABLED + ESP_LOGE(ROAMING_TAG, "No roaming trigger enabled. Roaming app cannot be de-initialized"); + return; +#endif + /* Unregister Event handlers */ + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, roaming_app_connected_event_handler)); + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, roaming_app_disconnected_event_handler)); +#if LOW_RSSI_ROAMING_ENABLED + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, + &roaming_app_rssi_low_handler)); +#endif /*ROAMING_LOW_RSSI_THRESHOLD*/ + +#if PERIODIC_RRM_MONITORING + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_NEIGHBOR_REP, + &roaming_app_neighbor_report_recv_handler)); +#endif /*PERIODIC_RRM_MONITORING*/ + + /* Disabling the periodic scan and RRM events */ +#if PERIODIC_RRM_MONITORING + g_roaming_app.periodic_rrm_active = false; +#endif /*PERIODIC_RRM_MONITORING*/ + +#if PERIODIC_SCAN_MONITORING + g_roaming_app.periodic_scan_active = false; +#endif /*PERIODIC_SCAN_MONITORING*/ + os_mutex_delete(neighbor_list_lock); + neighbor_list_lock = NULL; + os_mutex_delete(scan_results_lock); + scan_results_lock = NULL; +} diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 0913bd5e06..ebbe874f2f 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -226,6 +226,7 @@ idf_component_register(SRCS "${srcs}" "${esp_srcs}" "${tls_src}" "${roaming_src} "${crypto_src}" "${mbo_src}" "${dpp_src}" "${wps_registrar_src}" INCLUDE_DIRS include port/include esp_supplicant/include PRIV_INCLUDE_DIRS src src/utils esp_supplicant/src src/crypto + ../esp_wifi/wifi_apps/roaming_app/include LDFRAGMENTS ${linker_fragments} PRIV_REQUIRES mbedtls esp_timer esp_wifi) diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_common.c b/components/wpa_supplicant/esp_supplicant/src/esp_common.c index f84821d699..97f41ba758 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_common.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_common.c @@ -23,6 +23,9 @@ #include "rsn_supp/wpa_i.h" #include "rsn_supp/wpa.h" #include "esp_private/wifi.h" +#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP +#include "esp_roaming.h" +#endif /* Utility Functions */ esp_err_t esp_supplicant_str_to_mac(const char *str, uint8_t dest[6]) @@ -300,7 +303,7 @@ static int ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender, #ifdef CONFIG_MBO bool mbo_bss_profile_match(u8 *bssid) { - /* Incase supplicant wants drivers to skip this BSS, return false */ + /* In case supplicant wants drivers to skip this BSS, return false */ struct wpa_bss *bss = wpa_bss_get_bssid(&g_wpa_supp, bssid); if (!bss) { return true; @@ -419,6 +422,9 @@ void esp_supplicant_common_deinit(void) } s_supplicant_task_init_done = false; #endif /* CONFIG_SUPPLICANT_TASK */ +#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP + deinit_roaming_app(); +#endif #endif /* defined(CONFIG_IEEE80211KV) || defined(CONFIG_IEEE80211R) */ } @@ -732,6 +738,16 @@ static uint8_t get_extended_caps_ie(uint8_t *ie, size_t len) return ext_caps_ie_len + 2; } +#else /* CONFIG_IEEE80211KV */ +bool esp_rrm_is_rrm_supported_connection(void) +{ + return false; +} + +bool esp_wnm_is_btm_supported_connection(void) +{ + return false; +} #endif /* CONFIG_IEEE80211KV */ void esp_set_scan_ie(void) diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c index acde48189b..eb1066d557 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c @@ -39,6 +39,9 @@ #include "ap/sta_info.h" #include "wps/wps_defs.h" #include "wps/wps.h" +#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP +#include "esp_roaming.h" +#endif #ifdef CONFIG_DPP #include "common/dpp.h" @@ -469,6 +472,10 @@ int esp_supplicant_init(void) ret = esp_wifi_internal_wapi_init(); #endif +#if CONFIG_ESP_WIFI_ENABLE_ROAMING_APP + init_roaming_app(); +#endif + return ret; } diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 98e9c5237f..7355144003 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -219,7 +219,7 @@ INPUT = \ $(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_rrm.h \ $(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_wnm.h \ $(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_wps.h \ - $(PROJECT_PATH)/components/esp_wifi/wifi_apps/include/esp_nan.h \ + $(PROJECT_PATH)/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h \ $(PROJECT_PATH)/components/esp-tls/esp_tls_errors.h \ $(PROJECT_PATH)/components/esp-tls/esp_tls.h \ $(PROJECT_PATH)/components/fatfs/diskio/diskio_impl.h \ @@ -310,7 +310,7 @@ INPUT = \ $(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_dpp.h \ $(PROJECT_PATH)/components/wpa_supplicant/esp_supplicant/include/esp_supplicant_utils.h \ -## Target specific headers are in seperate Doxyfile files +## Target specific headers are in separate Doxyfile files @INCLUDE = $(PROJECT_PATH)/docs/doxygen/Doxyfile_$(IDF_TARGET) ## Get warnings for functions that have no documentation for their parameters or return value From 0d7c909e87330369cb0a9ad8e083c35be01eb232 Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Fri, 29 Mar 2024 18:40:49 +0530 Subject: [PATCH 10/12] feat(esp_wifi): Showcases the new scan by bitmap config - Showcases the ability to scan by the bitmap config newly added to the wifi_scan_config_t by implementing it in the scan config --- examples/wifi/scan/README.md | 5 ++-- examples/wifi/scan/main/Kconfig.projbuild | 7 +++++ examples/wifi/scan/main/scan.c | 33 ++++++++++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/examples/wifi/scan/README.md b/examples/wifi/scan/README.md index fc8dba9b12..38b1f8fbfa 100644 --- a/examples/wifi/scan/README.md +++ b/examples/wifi/scan/README.md @@ -23,7 +23,8 @@ Open the project configuration menu (`idf.py menuconfig`). In the `Example Configuration` menu: * Set the Example configuration. - * Use `Max size of scan list` to set the maximum nunber of access points in the list. + * Use `Max size of scan list` to set the maximum number of access points in the list. + * Use 'Scan Channel list' to list specific channels you wish to scan. For eg. 1,6,9,11. By Default all channels will be scanned. ### Build and Flash @@ -47,7 +48,7 @@ As you run the example, you will see the following log: I (443) wifi:wifi firmware version: 6bff005 I (443) wifi:wifi certification version: v7.0 I (443) wifi:config NVS flash: enabled -I (443) wifi:config nano formating: disabled +I (443) wifi:config nano formatting: disabled I (453) wifi:Init data frame dynamic rx buffer num: 32 I (453) wifi:Init management frame dynamic rx buffer num: 32 I (463) wifi:Init management short buffer num: 32 diff --git a/examples/wifi/scan/main/Kconfig.projbuild b/examples/wifi/scan/main/Kconfig.projbuild index 3529c9b01d..cd5d264b70 100644 --- a/examples/wifi/scan/main/Kconfig.projbuild +++ b/examples/wifi/scan/main/Kconfig.projbuild @@ -7,4 +7,11 @@ menu "Example Configuration" help The size of array that will be used to retrieve the list of access points. + config EXAMPLE_USE_SCAN_CHANNEL_BITMAP + bool "Scan only non overlapping channels using Channel bitmap" + default 0 + help + Enable this to scan only the non overlapping channels i.e 1,6,11 by mentioning a channel bitmap + in scan config. If you wish to scan a different set of specific channels, please edit the channel_list + array in scan.c. Channels for a 2.4 ghz network range should range from 1-14. endmenu diff --git a/examples/wifi/scan/main/scan.c b/examples/wifi/scan/main/scan.c index 02a7388366..19e2c41555 100644 --- a/examples/wifi/scan/main/scan.c +++ b/examples/wifi/scan/main/scan.c @@ -17,9 +17,16 @@ #include "esp_log.h" #include "esp_event.h" #include "nvs_flash.h" +#include "regex.h" #define DEFAULT_SCAN_LIST_SIZE CONFIG_EXAMPLE_SCAN_LIST_SIZE +#ifdef CONFIG_EXAMPLE_USE_SCAN_CHANNEL_BITMAP +#define USE_CHANNEL_BTIMAP 1 +#define CHANNEL_LIST_SIZE 3 +static uint8_t channel_list[CHANNEL_LIST_SIZE] = {1, 6, 11}; +#endif /*CONFIG_EXAMPLE_USE_SCAN_CHANNEL_BITMAP*/ + static const char *TAG = "scan"; static void print_auth_mode(int authmode) @@ -133,6 +140,17 @@ static void print_cipher_type(int pairwise_cipher, int group_cipher) } } +#ifdef USE_CHANNEL_BTIMAP +static void array_2_channel_bitmap(const uint8_t channel_list[], const uint8_t channel_list_size, wifi_scan_config_t *scan_config) { + + for(uint8_t i = 0; i < channel_list_size; i++) { + uint8_t channel = channel_list[i]; + scan_config->channel_bitmap.ghz_2_channels |= (1 << channel); + } +} +#endif /*USE_CHANNEL_BTIMAP*/ + + /* Initialize Wi-Fi as sta and set scan method */ static void wifi_scan(void) { @@ -149,9 +167,23 @@ static void wifi_scan(void) uint16_t ap_count = 0; memset(ap_info, 0, sizeof(ap_info)); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_start()); + +#ifdef USE_CHANNEL_BTIMAP + wifi_scan_config_t *scan_config = (wifi_scan_config_t *)calloc(1,sizeof(wifi_scan_config_t)); + if (!scan_config) { + ESP_LOGE(TAG, "Memory Allocation for scan config failed!"); + return; + } + array_2_channel_bitmap(channel_list, CHANNEL_LIST_SIZE, scan_config); + esp_wifi_scan_start(scan_config, true); + +#else esp_wifi_scan_start(NULL, true); +#endif /*USE_CHANNEL_BTIMAP*/ + ESP_LOGI(TAG, "Max AP number ap_info can hold = %u", number); ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info)); ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count)); @@ -165,7 +197,6 @@ static void wifi_scan(void) } ESP_LOGI(TAG, "Channel \t\t%d", ap_info[i].primary); } - } void app_main(void) From c5d8acc40a71316a4be889d5ce25695d6761928e Mon Sep 17 00:00:00 2001 From: jgujarathi Date: Fri, 29 Mar 2024 18:41:49 +0530 Subject: [PATCH 11/12] feat(esp_wifi): Showcase roaming_app capabalities through dedicated example - Shows use of roaming app through dedicated example : roaming_app_example. - Changes the location of the earlier roaming example by moving it into the roaming_11kvr. - Please note that this feature is still experimental and still being optimised and tuned. --- examples/wifi/README.md | 2 +- examples/wifi/roaming/README.md | 95 +---------- .../{ => roaming_11kvr}/CMakeLists.txt | 0 examples/wifi/roaming/roaming_11kvr/README.md | 95 +++++++++++ .../{ => roaming_11kvr}/main/CMakeLists.txt | 0 .../main/Kconfig.projbuild | 0 .../main/roaming_example.c | 11 +- .../roaming/{ => roaming_11kvr}/sdkconfig.ci | 0 .../{ => roaming_11kvr}/sdkconfig.defaults | 0 .../wifi/roaming/roaming_app/CMakeLists.txt | 6 + examples/wifi/roaming/roaming_app/README.md | 83 ++++++++++ .../roaming/roaming_app/main/CMakeLists.txt | 2 + .../roaming_app/main/Kconfig.projbuild | 14 ++ .../roaming_app/main/roaming_app_example.c | 149 ++++++++++++++++++ .../roaming/roaming_app/sdkconfig.defaults | 9 ++ tools/ci/check_copyright_ignore.txt | 1 - 16 files changed, 375 insertions(+), 92 deletions(-) rename examples/wifi/roaming/{ => roaming_11kvr}/CMakeLists.txt (100%) create mode 100644 examples/wifi/roaming/roaming_11kvr/README.md rename examples/wifi/roaming/{ => roaming_11kvr}/main/CMakeLists.txt (100%) rename examples/wifi/roaming/{ => roaming_11kvr}/main/Kconfig.projbuild (100%) rename examples/wifi/roaming/{ => roaming_11kvr}/main/roaming_example.c (97%) rename examples/wifi/roaming/{ => roaming_11kvr}/sdkconfig.ci (100%) rename examples/wifi/roaming/{ => roaming_11kvr}/sdkconfig.defaults (100%) create mode 100644 examples/wifi/roaming/roaming_app/CMakeLists.txt create mode 100644 examples/wifi/roaming/roaming_app/README.md create mode 100644 examples/wifi/roaming/roaming_app/main/CMakeLists.txt create mode 100644 examples/wifi/roaming/roaming_app/main/Kconfig.projbuild create mode 100644 examples/wifi/roaming/roaming_app/main/roaming_app_example.c create mode 100644 examples/wifi/roaming/roaming_app/sdkconfig.defaults diff --git a/examples/wifi/README.md b/examples/wifi/README.md index c9ccd580e8..cdd1be113a 100644 --- a/examples/wifi/README.md +++ b/examples/wifi/README.md @@ -15,7 +15,7 @@ The examples are grouped into sub-directories by category. Each category directo * `getting_started` contains the basic examples for using Wi-Fi in STA and AP mode. * `iperf` shows the common performance measurements for two ESP running this example. * `power_save` contains a how to use power save mode of Wi-Fi. -* `roaming` contains a example using 802.11k and 802.11v APIs. +* `roaming` contains 2 examples that demonstrate roaming capabilities. * `scan` shows how to scan for all the available APs. * `smart_config` shows how ESP connects to a target AP with ESPTOUCH. * `wifi_eap_fast` shows how ESP connects to AP with Wi-Fi enterprise encryption using the EAP-FAST method. diff --git a/examples/wifi/roaming/README.md b/examples/wifi/roaming/README.md index 77b69d89fe..ae4594a70e 100644 --- a/examples/wifi/roaming/README.md +++ b/examples/wifi/roaming/README.md @@ -1,95 +1,16 @@ | Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | | ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -# Roaming Example +# Wi-Fi Roaming Examples -(See the README.md file in the upper level 'examples' directory for more information about examples.) +This directory contains 2 example ESP-IDF projects that demonstrate the Wi-Fi Roaming capabilities. These are intended to demonstrate the Wi-Fi roaming functionality, and to provide code that you can copy and use in your own projects. -This example demonstrate a roaming example using 11k and 11v APIs. +# Example Layout -## How to use example +* `roaming_11kvr` demonstrates the use of API's that implement IEEE 802.11kvr mechanisms. +* `roaming_app` implements Espressif's roaming app that optimally combines legacy roaming and 802.11kv based + roaming for effective roaming. -Before project configuration and build, be sure to set the correct chip target using `idf.py set-target `. +# More -### Hardware Required - -* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.) -* A USB cable for Power supply and programming - -### Build and Flash - -Build the project and flash it to the board, then run the monitor tool to view the serial output: - -Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. - -(To exit the serial monitor, type ``Ctrl-]``.) - -See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects. - -* [ESP-IDF Getting Started Guide on ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html) -* [ESP-IDF Getting Started Guide on ESP32-S2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html) -* [ESP-IDF Getting Started Guide on ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/get-started/index.html) - -## Example Output - -All the logs are taken after debug enabled: - -Beacon request: -``` -I (11125) wpa: scan issued at time=7579442263 -I (11125) wpa: BSS: Add new id 1 BSSID b6:fb:e4:4d:6e:22 SSID 'ESPTest' chan 1 -I (11135) wpa: BSS: Add new id 2 BSSID d6:fb:e4:4d:6e:22 SSID 'ESPDedicated' chan 1 -I (11165) wpa: BSS: Add new id 3 BSSID c6:fb:e4:4d:6e:22 SSID 'ESPGuestNetwork' chan 1 -I (11185) wpa: BSS: Add new id 4 BSSID b4:fb:e4:4d:6e:22 SSID 'ESPIndia' chan 1 -I (11245) wpa: BSS: Add new id 5 BSSID b8:27:eb:3b:4a:59 SSID 'wpa2_enterprise' chan 6 -I (11365) wpa: BSS: Add new id 6 BSSID 38:94:ed:34:07:66 SSID 'Nighthawk' chan 6 -I (11365) wpa: BSS: Add new id 7 BSSID 10:da:43:dc:99:20 SSID 'NETGEAR40' chan 6 -I (12435) wpa: BSS: Add new id 8 BSSID c6:fb:e4:4d:7c:89 SSID 'ESPGuestNetwork' chan 11 -I (12445) wpa: BSS: Add new id 9 BSSID d6:fb:e4:4d:7c:89 SSID 'ESPDedicated' chan 11 -I (12455) wpa: BSS: Add new id 10 BSSID b6:fb:e4:4d:7c:89 SSID 'ESPTest' chan 11 -I (12485) wpa: BSS: Add new id 11 BSSID b4:fb:e4:4d:7c:89 SSID 'ESPIndia' chan 11 -I (12825) wpa: scan done received -I (12845) wpa: action frame sent -I (12845) wpa: action frame sent -``` - -Sta's moving from one AP to another on BTM request: - -``` -I (379479) wpa: WNM: RX action 7 from 50:3e:aa:26:35:42 -I (379479) wpa: WNM: BSS Transition Management Request: dialog_token=1 request_mode=0x1 disassoc_timer=0 validity_interval=1 -I (379489) wpa: WNM: Neighbor report tag 52 -I (379499) wpa: WNM: Subelement id=3 len=1 -I (379499) wpa: WNM: BSS Transition Candidate List -I (379509) wpa: 0: b4:e6:2d:eb:1d:76 info=0x0 op_class=81 chan=1 phy=7 pref=255 -I (379519) wpa: WNM: Candidate list valid for 102 ms -I (379519) wpa: WNM: Scan only for a specific BSSID since there is only a single candidate b4:e6:2d:eb:1d:76 -I (379539) wpa: scan issued at time=9979439869 -I (379539) wpa: BSS: Add new id 12 BSSID b4:e6:2d:eb:1d:76 SSID 'roaming_test123' chan 6 -I (381979) wpa: scan done received -I (381979) wpa: WNM: Process scan results for BSS Transition Management -I (381979) wpa: WNM: Found an acceptable preferred transition candidate BSS b4:e6:2d:eb:1d:76 (RSSI -7) -I (381989) wpa: WNM: Transition to BSS b4:e6:2d:eb:1d:76 based on BSS Transition Management Request after_new_scan=1) -I (381999) wpa: WNM: Sending successful BSS Transition Management Response -I (382009) wpa: WNM: Send BSS Transition Management Response to 50:3e:aa:26:35:42 dialog_token=1 status=0 reason=0 delay=0 -I (382019) wpa: action frame sent -I (382029) wpa: WNM: Issuing connect -I (382029) wifi:state: run -> init (0) -I (382029) wifi:pm stop, total sleep time: 102136757 us / 109258805 us - -I (382039) wifi:new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1 -I (382769) wifi:new:<6,1>, old:<6,0>, ap:<255,255>, sta:<6,1>, prof:1 -I (384689) wifi:state: init -> auth (b0) -I (384699) wifi:state: auth -> assoc (0) -I (384709) wifi:state: assoc -> run (10) -I (384729) wifi:connected with roaming_test123, aid = 1, channel 6, 40U, bssid = b4:e6:2d:eb:1d:76 -I (384729) wifi:security: WPA2-PSK, phy: bgn, rssi: -7 -I (384729) wifi:pm start, type: 1 - -I (384789) wifi:AP's beacon interval = 102400 us, DTIM period = 2 -I (385609) esp_netif_handlers: sta ip: 192.168.4.2, mask: 255.255.255.0, gw: 192.168.4.1 -``` - -## Troubleshooting - -For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. +See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples. diff --git a/examples/wifi/roaming/CMakeLists.txt b/examples/wifi/roaming/roaming_11kvr/CMakeLists.txt similarity index 100% rename from examples/wifi/roaming/CMakeLists.txt rename to examples/wifi/roaming/roaming_11kvr/CMakeLists.txt diff --git a/examples/wifi/roaming/roaming_11kvr/README.md b/examples/wifi/roaming/roaming_11kvr/README.md new file mode 100644 index 0000000000..77b69d89fe --- /dev/null +++ b/examples/wifi/roaming/roaming_11kvr/README.md @@ -0,0 +1,95 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | + +# Roaming Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example demonstrate a roaming example using 11k and 11v APIs. + +## How to use example + +Before project configuration and build, be sure to set the correct chip target using `idf.py set-target `. + +### Hardware Required + +* A development board with ESP32/ESP32-S2/ESP32-C3 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.) +* A USB cable for Power supply and programming + +### Build and Flash + +Build the project and flash it to the board, then run the monitor tool to view the serial output: + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects. + +* [ESP-IDF Getting Started Guide on ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html) +* [ESP-IDF Getting Started Guide on ESP32-S2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html) +* [ESP-IDF Getting Started Guide on ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/get-started/index.html) + +## Example Output + +All the logs are taken after debug enabled: + +Beacon request: +``` +I (11125) wpa: scan issued at time=7579442263 +I (11125) wpa: BSS: Add new id 1 BSSID b6:fb:e4:4d:6e:22 SSID 'ESPTest' chan 1 +I (11135) wpa: BSS: Add new id 2 BSSID d6:fb:e4:4d:6e:22 SSID 'ESPDedicated' chan 1 +I (11165) wpa: BSS: Add new id 3 BSSID c6:fb:e4:4d:6e:22 SSID 'ESPGuestNetwork' chan 1 +I (11185) wpa: BSS: Add new id 4 BSSID b4:fb:e4:4d:6e:22 SSID 'ESPIndia' chan 1 +I (11245) wpa: BSS: Add new id 5 BSSID b8:27:eb:3b:4a:59 SSID 'wpa2_enterprise' chan 6 +I (11365) wpa: BSS: Add new id 6 BSSID 38:94:ed:34:07:66 SSID 'Nighthawk' chan 6 +I (11365) wpa: BSS: Add new id 7 BSSID 10:da:43:dc:99:20 SSID 'NETGEAR40' chan 6 +I (12435) wpa: BSS: Add new id 8 BSSID c6:fb:e4:4d:7c:89 SSID 'ESPGuestNetwork' chan 11 +I (12445) wpa: BSS: Add new id 9 BSSID d6:fb:e4:4d:7c:89 SSID 'ESPDedicated' chan 11 +I (12455) wpa: BSS: Add new id 10 BSSID b6:fb:e4:4d:7c:89 SSID 'ESPTest' chan 11 +I (12485) wpa: BSS: Add new id 11 BSSID b4:fb:e4:4d:7c:89 SSID 'ESPIndia' chan 11 +I (12825) wpa: scan done received +I (12845) wpa: action frame sent +I (12845) wpa: action frame sent +``` + +Sta's moving from one AP to another on BTM request: + +``` +I (379479) wpa: WNM: RX action 7 from 50:3e:aa:26:35:42 +I (379479) wpa: WNM: BSS Transition Management Request: dialog_token=1 request_mode=0x1 disassoc_timer=0 validity_interval=1 +I (379489) wpa: WNM: Neighbor report tag 52 +I (379499) wpa: WNM: Subelement id=3 len=1 +I (379499) wpa: WNM: BSS Transition Candidate List +I (379509) wpa: 0: b4:e6:2d:eb:1d:76 info=0x0 op_class=81 chan=1 phy=7 pref=255 +I (379519) wpa: WNM: Candidate list valid for 102 ms +I (379519) wpa: WNM: Scan only for a specific BSSID since there is only a single candidate b4:e6:2d:eb:1d:76 +I (379539) wpa: scan issued at time=9979439869 +I (379539) wpa: BSS: Add new id 12 BSSID b4:e6:2d:eb:1d:76 SSID 'roaming_test123' chan 6 +I (381979) wpa: scan done received +I (381979) wpa: WNM: Process scan results for BSS Transition Management +I (381979) wpa: WNM: Found an acceptable preferred transition candidate BSS b4:e6:2d:eb:1d:76 (RSSI -7) +I (381989) wpa: WNM: Transition to BSS b4:e6:2d:eb:1d:76 based on BSS Transition Management Request after_new_scan=1) +I (381999) wpa: WNM: Sending successful BSS Transition Management Response +I (382009) wpa: WNM: Send BSS Transition Management Response to 50:3e:aa:26:35:42 dialog_token=1 status=0 reason=0 delay=0 +I (382019) wpa: action frame sent +I (382029) wpa: WNM: Issuing connect +I (382029) wifi:state: run -> init (0) +I (382029) wifi:pm stop, total sleep time: 102136757 us / 109258805 us + +I (382039) wifi:new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1 +I (382769) wifi:new:<6,1>, old:<6,0>, ap:<255,255>, sta:<6,1>, prof:1 +I (384689) wifi:state: init -> auth (b0) +I (384699) wifi:state: auth -> assoc (0) +I (384709) wifi:state: assoc -> run (10) +I (384729) wifi:connected with roaming_test123, aid = 1, channel 6, 40U, bssid = b4:e6:2d:eb:1d:76 +I (384729) wifi:security: WPA2-PSK, phy: bgn, rssi: -7 +I (384729) wifi:pm start, type: 1 + +I (384789) wifi:AP's beacon interval = 102400 us, DTIM period = 2 +I (385609) esp_netif_handlers: sta ip: 192.168.4.2, mask: 255.255.255.0, gw: 192.168.4.1 +``` + +## Troubleshooting + +For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/wifi/roaming/main/CMakeLists.txt b/examples/wifi/roaming/roaming_11kvr/main/CMakeLists.txt similarity index 100% rename from examples/wifi/roaming/main/CMakeLists.txt rename to examples/wifi/roaming/roaming_11kvr/main/CMakeLists.txt diff --git a/examples/wifi/roaming/main/Kconfig.projbuild b/examples/wifi/roaming/roaming_11kvr/main/Kconfig.projbuild similarity index 100% rename from examples/wifi/roaming/main/Kconfig.projbuild rename to examples/wifi/roaming/roaming_11kvr/main/Kconfig.projbuild diff --git a/examples/wifi/roaming/main/roaming_example.c b/examples/wifi/roaming/roaming_11kvr/main/roaming_example.c similarity index 97% rename from examples/wifi/roaming/main/roaming_example.c rename to examples/wifi/roaming/roaming_11kvr/main/roaming_example.c index fce9fbe0d9..3e47cbb158 100644 --- a/examples/wifi/roaming/main/roaming_example.c +++ b/examples/wifi/roaming/roaming_11kvr/main/roaming_example.c @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ #include #include #include @@ -189,7 +194,7 @@ static char * get_btm_neighbor_list(uint8_t *report, size_t report_len) pos += s_len; } - ESP_LOGI(TAG, "RMM neigbor report bssid=" MACSTR + ESP_LOGI(TAG, "RMM neighbor report bssid=" MACSTR " info=0x%" PRIx32 " op_class=%u chan=%u phy_type=%u%s%s%s%s", MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN), nr[ETH_ALEN + 4], nr[ETH_ALEN + 5], @@ -310,11 +315,11 @@ char * get_tmp_neighbor_list(uint8_t *report, size_t report_len) static void esp_neighbor_report_recv_handler(void* arg, esp_event_base_t event_base,int32_t event_id, void* event_data) { if (!g_neighbor_report_active) { - ESP_LOGV(TAG,"Neighbor report recieved but not triggerred by us"); + ESP_LOGV(TAG,"Neighbor report received but not triggered by us"); return; } if (!event_data) { - ESP_LOGE(TAG, "No event data recieved for neighbor report"); + ESP_LOGE(TAG, "No event data received for neighbor report"); return; } g_neighbor_report_active = false; diff --git a/examples/wifi/roaming/sdkconfig.ci b/examples/wifi/roaming/roaming_11kvr/sdkconfig.ci similarity index 100% rename from examples/wifi/roaming/sdkconfig.ci rename to examples/wifi/roaming/roaming_11kvr/sdkconfig.ci diff --git a/examples/wifi/roaming/sdkconfig.defaults b/examples/wifi/roaming/roaming_11kvr/sdkconfig.defaults similarity index 100% rename from examples/wifi/roaming/sdkconfig.defaults rename to examples/wifi/roaming/roaming_11kvr/sdkconfig.defaults diff --git a/examples/wifi/roaming/roaming_app/CMakeLists.txt b/examples/wifi/roaming/roaming_app/CMakeLists.txt new file mode 100644 index 0000000000..33e8cd4d68 --- /dev/null +++ b/examples/wifi/roaming/roaming_app/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(wifi_roaming_app) diff --git a/examples/wifi/roaming/roaming_app/README.md b/examples/wifi/roaming/roaming_app/README.md new file mode 100644 index 0000000000..dfe1b1d1c4 --- /dev/null +++ b/examples/wifi/roaming/roaming_app/README.md @@ -0,0 +1,83 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | + +# Wi-Fi Station Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example shows how to use the Wi-Fi Roaming App functionality of the Wi-Fi driver of ESP for efficient roaming between compatible APs. + +## How to use example + +### Configure the project + +Open the project configuration menu (`idf.py menuconfig`). + +In the `Example Configuration` menu: + +* Set the Wi-Fi configuration. + * Set `WiFi SSID`. + * Set `WiFi Password`. + +* To better understand how to configure the app in Components --> Wi-Fi --> Roaming App settings, + please go read the README at components/esp_wifi/wifi_apps/roaming_app/src. +Optional: If you need, change the other options according to your requirements. + +### Build and Flash + +Build the project and flash it to the board, then run the monitor tool to view the serial output: + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects. + +* [ESP-IDF Getting Started Guide on ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html) +* [ESP-IDF Getting Started Guide on ESP32-S2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html) +* [ESP-IDF Getting Started Guide on ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/get-started/index.html) + +## Example Output +Note that the output, in particular the order of the output, may vary depending on the environment. + +Console output if station roams between APs successfully: +``` +I (827) wifi roaming app: wifi_init_sta finished. +I (837) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:5 +I (837) wifi:state: init -> auth (b0) +I (847) wifi:state: auth -> assoc (0) +I (857) wifi:Association refused temporarily, comeback time 1000 (TUs) +I (1877) wifi:state: assoc -> assoc (0) +I (1917) wifi:state: assoc -> run (10) +I (2997) wifi:connected with roaming_setup, aid = 1, channel 1, BW20, bssid = de:bd:5b:93:55:99 +I (2997) wifi:security: WPA2-PSK, phy: bgn, rssi: -58 +I (3007) wifi:pm start, type: 1 + +I (3087) wifi:AP's beacon interval = 102400 us, DTIM period = 2 +I (10467) ROAM: roaming_app_rssi_low_handler:bss rssi is=-61 +I (10467) ROAM: Issued Scan +I (10857) ROAM: Could not find a better AP with the threshold set to 1 +I (63017) ROAM: Issued Scan +I (63347) ROAM: Found a better AP 7c:50:79:06:18:46 at channel 9 +I (63347) wifi:state: run -> init (cc0) +I (63347) wifi:pm stop, total sleep time: 54401305 us / 60339946 us + +I (63347) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:5 +E (63357) ROAM: Neighbor report is empty +I (63357) wifi roaming app: station disconnected during roaming +I (63377) wifi roaming app: connect to the AP fail +I (63377) ROAM: Disconnecting and connecting to 7c:50:79:06:18:46 on account of better rssi +I (63497) wifi:new:<9,0>, old:<1,0>, ap:<255,255>, sta:<9,0>, prof:5 +I (63837) wifi:state: init -> auth (b0) +I (63847) wifi:state: auth -> assoc (0) +I (63887) wifi:Association refused temporarily, comeback time 1000 (TUs) +I (64907) wifi:state: assoc -> assoc (0) +I (64977) wifi:state: assoc -> run (10) +I (65007) wifi:connected with roaming_setup, aid = 1, channel 9, BW20, bssid = 7c:50:79:06:18:46 +I (65007) wifi:security: WPA2-PSK, phy: bgn, rssi: -32 +I (65027) wifi:pm start, type: 1 +``` + +## Troubleshooting + +For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/wifi/roaming/roaming_app/main/CMakeLists.txt b/examples/wifi/roaming/roaming_app/main/CMakeLists.txt new file mode 100644 index 0000000000..22b82c0587 --- /dev/null +++ b/examples/wifi/roaming/roaming_app/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "roaming_app_example.c" + INCLUDE_DIRS ".") diff --git a/examples/wifi/roaming/roaming_app/main/Kconfig.projbuild b/examples/wifi/roaming/roaming_app/main/Kconfig.projbuild new file mode 100644 index 0000000000..87525bc12a --- /dev/null +++ b/examples/wifi/roaming/roaming_app/main/Kconfig.projbuild @@ -0,0 +1,14 @@ +menu "Example Configuration" + + config ESP_WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config ESP_WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. +endmenu diff --git a/examples/wifi/roaming/roaming_app/main/roaming_app_example.c b/examples/wifi/roaming/roaming_app/main/roaming_app_example.c new file mode 100644 index 0000000000..c9c8de2c41 --- /dev/null +++ b/examples/wifi/roaming/roaming_app/main/roaming_app_example.c @@ -0,0 +1,149 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* WiFi Roaming App Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#include "lwip/err.h" +#include "lwip/sys.h" + +/* The examples use WiFi configuration that you can set via project configuration menu + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID +#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD + +/* FreeRTOS event group to signal when we are connected*/ +static EventGroupHandle_t s_wifi_event_group; + +/* The event group allows multiple bits for each event, but we only care about two events: + * - we are connected to the AP with an IP + * - we failed to connect after the maximum amount of retries */ +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 +#define MAXIMUM_RETRY 5 +const char *TAG = "wifi roaming app"; + +static int s_retry_num = 0; + +static void event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + wifi_event_sta_disconnected_t *disconn = event_data; + if (disconn->reason == WIFI_REASON_ROAMING) { + ESP_LOGI(TAG, "station disconnected during roaming"); + } else { + if (s_retry_num < MAXIMUM_RETRY) { + ESP_LOGI(TAG, "station disconnected with reason %d", disconn->reason); + esp_wifi_connect(); + s_retry_num++; + ESP_LOGI(TAG, "retry to connect to the AP"); + } else { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + } + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + s_retry_num = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } +} + +void wifi_init_sta(void) +{ + s_wifi_event_group = xEventGroupCreate(); + + ESP_ERROR_CHECK(esp_netif_init()); + + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + ESP_EVENT_ANY_ID, + &event_handler, + NULL, + &instance_any_id)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, + IP_EVENT_STA_GOT_IP, + &event_handler, + NULL, + &instance_got_ip)); + + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_ESP_WIFI_SSID, + .password = EXAMPLE_ESP_WIFI_PASS, + .btm_enabled = 1, + .rm_enabled = 1, + .mbo_enabled = 1, + .ft_enabled = 1, + } + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + ESP_LOGI(TAG, "wifi_init_sta finished."); + + /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum + * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, + WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, + pdFALSE, + pdFALSE, + portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & WIFI_CONNECTED_BIT) { + ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", + EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else if (bits & WIFI_FAIL_BIT) { + ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", + EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else { + ESP_LOGE(TAG, "UNEXPECTED EVENT"); + } +} + +void app_main(void) +{ + //Initialize NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_LOGI(TAG, "ESP_WIFI_MODE_STA"); + wifi_init_sta(); +} diff --git a/examples/wifi/roaming/roaming_app/sdkconfig.defaults b/examples/wifi/roaming/roaming_app/sdkconfig.defaults new file mode 100644 index 0000000000..b236574e3d --- /dev/null +++ b/examples/wifi/roaming/roaming_app/sdkconfig.defaults @@ -0,0 +1,9 @@ +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + + +CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n +CONFIG_ESP_WIFI_11KV_SUPPORT=y +CONFIG_ESP_WIFI_SCAN_CACHE=y +CONFIG_ESP_WIFI_MBO_SUPPORT=y +CONFIG_ESP_WIFI_ENABLE_ROAMING_APP=y +CONFIG_ESP_WIFI_11R_SUPPORT=y diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index c1528ca06c..38292824f0 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1189,7 +1189,6 @@ examples/wifi/getting_started/softAP/main/softap_example_main.c examples/wifi/getting_started/station/main/station_example_main.c examples/wifi/iperf/main/iperf_example_main.c examples/wifi/power_save/main/power_save.c -examples/wifi/roaming/main/roaming_example.c examples/wifi/scan/main/scan.c examples/wifi/smart_config/main/smartconfig_main.c examples/wifi/wifi_easy_connect/dpp-enrollee/main/dpp_enrollee_main.c From 431a2a7d1c311867b4f1995312b15ef72648c224 Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Mon, 1 Apr 2024 17:14:01 +0530 Subject: [PATCH 12/12] fix(esp_wifi): Address review comments --- components/esp_wifi/Kconfig | 6 +++--- components/esp_wifi/include/esp_wifi_types_generic.h | 5 +---- components/esp_wifi/lib | 2 +- .../esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming | 2 ++ components/esp_wifi/wifi_apps/roaming_app/src/README.md | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index 72157799a7..d725193d96 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -585,9 +585,9 @@ menu "Wi-Fi" best APs, handling low rssi events etc. Risk Warning - - Please note that this feature is still experimental and enabling this potentially can - lead to unpredictable scanning, connection and roaming attempts. - We are still working on tuning and optimising this feature to ensure reliable and stable use. + Please note that this feature is still experimental and enabling this potentially can + lead to unpredictable scanning, connection and roaming attempts. + We are still working on tuning and optimising this feature to ensure reliable and stable use. menu "Configure roaming App" depends on ESP_WIFI_ENABLE_ROAMING_APP diff --git a/components/esp_wifi/include/esp_wifi_types_generic.h b/components/esp_wifi/include/esp_wifi_types_generic.h index 7f0a1b40ab..ae35cb2752 100644 --- a/components/esp_wifi/include/esp_wifi_types_generic.h +++ b/components/esp_wifi/include/esp_wifi_types_generic.h @@ -166,10 +166,7 @@ typedef struct { typedef struct { uint16_t ghz_2_channels; /**< Represents 2.4 GHz channels */ - uint8_t unii1_channels; /**< UNII-1 channels */ - uint8_t unii2_channels; /**< UNII-2 channels */ - uint16_t unii2e_channels; /**< UNII-2 Extended channels */ - uint8_t unii3_channels; /**< UNII-3 channels */ + uint32_t ghz_5_channels; /**< Represents 5 GHz channels */ } wifi_scan_channel_bitmap_t; /** @brief Parameters for an SSID scan. */ diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index 8fde018db0..1b1ab64e64 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit 8fde018db003c7918802d31d14a22eea58c9ba86 +Subproject commit 1b1ab64e64b69d25e58e740b8bf556519b1730f8 diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming index aa79fbdf93..3effeed781 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming +++ b/components/esp_wifi/wifi_apps/roaming_app/src/Kconfig.roaming @@ -26,6 +26,8 @@ menu "Roaming triggers" after each failed attempt to roam. This allows for the station to keep scanning for better AP's after the Low RSSI threshold is reached in a stepped manner, rather than only attempting to roam the first time the current AP's RSSI breaches the set RSSI threshold. + Setting 0 here may cause station to be flooded with low rssi events, + therefore that's not recommended to be kept. config ESP_WIFI_ROAMING_PERIODIC_SCAN_MONITOR bool "Conduct periodic scans to check if a better AP is available" diff --git a/components/esp_wifi/wifi_apps/roaming_app/src/README.md b/components/esp_wifi/wifi_apps/roaming_app/src/README.md index fae12e93a9..182d621929 100644 --- a/components/esp_wifi/wifi_apps/roaming_app/src/README.md +++ b/components/esp_wifi/wifi_apps/roaming_app/src/README.md @@ -70,7 +70,7 @@ Successive roaming attempts by multiple roaming triggers simultaneously could le **Periodic Neighbor Report Requests:** -Neighbor Report requests are a part of the IEEE 802.11k specification, and hence the intended network would need to support and be setup in a way to support its mechanisms. Periodic neighbor report requests provide vital information about the network and other APs in the vicinity that are candidate APs of the same network. This can be enabled using “Send Periodic Neighbor Report requests for updating the internal list”. he frequency of these requests is controlled by “Time interval between Periodic Neighbor report Requests”. There can also be a RSSI threshold (“Threshold for sending periodic neighbor report requests”) after which you wish to consider sending these requests however this is set by default to 0. +Neighbor Report requests are a part of the IEEE 802.11k specification, and hence the intended network would need to support and be setup in a way to support its mechanisms. Periodic neighbor report requests provide vital information about the network and other APs in the vicinity that are candidate APs of the same network. This can be enabled using “Send Periodic Neighbor Report requests for updating the internal list”. The frequency of these requests is controlled by “Time interval between Periodic Neighbor report Requests”. There can also be a RSSI threshold (“Threshold for sending periodic neighbor report requests”) after which you wish to consider sending these requests however this is set by default to -20. **Notes :**