Parcourir la source

Merge branch 'feature/mbo_support' into 'master'

esp_wifi: Add support for MBO certification

Closes WIFI-3777

See merge request espressif/esp-idf!12650
Jiang Jiang Jian il y a 4 ans
Parent
commit
f05351497c
28 fichiers modifiés avec 1605 ajouts et 193 suppressions
  1. 8 14
      components/esp_wifi/include/esp_wifi_types.h
  2. 1 1
      components/esp_wifi/lib
  3. 9 1
      components/wpa_supplicant/CMakeLists.txt
  4. 14 8
      components/wpa_supplicant/Kconfig
  5. 6 0
      components/wpa_supplicant/component.mk
  6. 64 0
      components/wpa_supplicant/esp_supplicant/include/esp_mbo.h
  7. 10 18
      components/wpa_supplicant/esp_supplicant/include/esp_wnm.h
  8. 235 57
      components/wpa_supplicant/esp_supplicant/src/esp_common.c
  9. 13 22
      components/wpa_supplicant/esp_supplicant/src/esp_common_i.h
  10. 20 29
      components/wpa_supplicant/esp_supplicant/src/esp_scan.c
  11. 8 15
      components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h
  12. 9 14
      components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c
  13. 30 4
      components/wpa_supplicant/src/common/bss.c
  14. 7 0
      components/wpa_supplicant/src/common/bss.h
  15. 36 0
      components/wpa_supplicant/src/common/ieee802_11_common.c
  16. 2 0
      components/wpa_supplicant/src/common/ieee802_11_common.h
  17. 53 0
      components/wpa_supplicant/src/common/ieee802_11_defs.h
  18. 799 0
      components/wpa_supplicant/src/common/mbo.c
  19. 7 0
      components/wpa_supplicant/src/common/rrm.c
  20. 148 1
      components/wpa_supplicant/src/common/wnm_sta.c
  21. 12 0
      components/wpa_supplicant/src/common/wnm_sta.h
  22. 0 1
      components/wpa_supplicant/src/common/wpa_common.c
  23. 2 0
      components/wpa_supplicant/src/common/wpa_common.h
  24. 60 1
      components/wpa_supplicant/src/common/wpa_supplicant_i.h
  25. 15 0
      components/wpa_supplicant/src/drivers/driver.h
  26. 35 0
      examples/wifi/roaming/main/roaming_example.c
  27. 1 0
      examples/wifi/roaming/sdkconfig.defaults
  28. 1 7
      tools/ci/check_copyright_ignore.txt

+ 8 - 14
components/esp_wifi/include/esp_wifi_types.h

@@ -1,16 +1,8 @@
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
 
 
 #ifndef __ESP_WIFI_TYPES_H__
@@ -80,6 +72,7 @@ typedef enum {
     WIFI_REASON_ASSOC_NOT_AUTHED         = 9,
     WIFI_REASON_DISASSOC_PWRCAP_BAD      = 10,
     WIFI_REASON_DISASSOC_SUPCHAN_BAD     = 11,
+    WIFI_REASON_BSS_TRANSITION_DISASSOC  = 12,
     WIFI_REASON_IE_INVALID               = 13,
     WIFI_REASON_MIC_FAILURE              = 14,
     WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT   = 15,
@@ -250,7 +243,8 @@ typedef struct {
     wifi_pmf_config_t pmf_cfg;    /**< Configuration for Protected Management Frame. Will be advertized 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 reserved:30;         /**< Reserved for future feature set */
+    uint32_t mbo_enabled:1;       /**< Whether MBO is enabled for the connection */
+    uint32_t reserved:29;         /**< Reserved for future feature set */
 } wifi_sta_config_t;
 
 /** @brief Configuration data for ESP32 AP or STA.

+ 1 - 1
components/esp_wifi/lib

@@ -1 +1 @@
-Subproject commit 4e1c7a8114fd03b9841f78a218f579799cb61a0a
+Subproject commit 6f9d2a11850b9fc0d4fd4ce89c492129e06fd77c

+ 9 - 1
components/wpa_supplicant/CMakeLists.txt

@@ -154,8 +154,13 @@ if(CONFIG_WPA_11KV_SUPPORT)
 else()
     set(roaming_src "")
 endif()
+if(CONFIG_WPA_MBO_SUPPORT)
+    set(mbo_src "src/common/mbo.c")
+else()
+    set(mbo_src "")
+endif()
 
-idf_component_register(SRCS "${srcs}" ${esp_srcs} "${tls_src}" "${roaming_src}" "${crypto_src}"
+idf_component_register(SRCS "${srcs}" ${esp_srcs} "${tls_src}" "${roaming_src}" "${crypto_src}" "${mbo_src}"
                     INCLUDE_DIRS include port/include esp_supplicant/include
                     PRIV_INCLUDE_DIRS src src/utils esp_supplicant/src
                     PRIV_REQUIRES mbedtls esp_timer)
@@ -190,4 +195,7 @@ if(CONFIG_WPA_WPS_STRICT)
     target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_WPS_STRICT)
 endif()
 
+if(CONFIG_WPA_MBO_SUPPORT)
+    target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_MBO)
+endif()
 set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY LINK_INTERFACE_MULTIPLICITY 3)

+ 14 - 8
components/wpa_supplicant/Kconfig

@@ -59,12 +59,18 @@ menu "Supplicant"
             and on the radio environment. Current implementation adds beacon report,
             link measurement, neighbor report.
 
-    if WPA_11KV_SUPPORT
-        config WPA_SCAN_CACHE
-            bool "Keep scan results in cache"
-            default n
-            help
-                Keep scan results in cache, if not enabled, those
-                will be flushed immediately.
-    endif
+    menuconfig WPA_SCAN_CACHE
+        bool "Keep scan results in cache"
+        depends on WPA_11KV_SUPPORT
+        default n
+        help
+            Keep scan results in cache, if not enabled, those
+            will be flushed immediately.
+
+    menuconfig WPA_MBO_SUPPORT
+        bool "Enable MBO support in supplicant"
+        depends on WPA_11KV_SUPPORT
+        default n
+        help
+            Select this option to enable WiFi Multiband operation certification support.
 endmenu

+ 6 - 0
components/wpa_supplicant/component.mk

@@ -70,6 +70,9 @@ ifneq ($(CONFIG_WPA_11KV_SUPPORT), y)
     esp_supplicant/src/esp_common.o \
     esp_supplicant/src/esp_scan.o
 endif
+ifneq ($(CONFIG_WPA_MBO_SUPPORT), y)
+    COMPONENT_OBJEXCLUDE += src/common/mbo.o
+endif
 
 CFLAGS += -DCONFIG_SHA256 -DCONFIG_DPP -DCONFIG_IEEE80211W -DESP_SUPPLICANT -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_TLS -DEAP_TTLS -DEAP_PEAP -DEAP_MSCHAPv2 -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -DCONFIG_ECC -DCONFIG_WNM -D__ets__ -Wno-strict-aliasing
 
@@ -79,3 +82,6 @@ endif
 ifdef CONFIG_WPA_WPS_STRICT
 	CFLAGS += -DCONFIG_WPS_STRICT
 endif
+ifdef CONFIG_WPA_MBO_SUPPORT
+	CFLAGS += -DCONFIG_MBO
+endif

+ 64 - 0
components/wpa_supplicant/esp_supplicant/include/esp_mbo.h

@@ -0,0 +1,64 @@
+/*
+ * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _ESP_MBO_H
+#define _ESP_MBO_H
+
+#include <stdbool.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+  * enum non_pref_chan_reason: Reason for non preference of channel
+  */
+enum non_pref_chan_reason {
+	NON_PREF_CHAN_REASON_UNSPECIFIED = 0,
+	NON_PREF_CHAN_REASON_RSSI = 1,
+	NON_PREF_CHAN_REASON_EXT_INTERFERENCE = 2,
+	NON_PREF_CHAN_REASON_INT_INTERFERENCE = 3,
+};
+
+/**
+  * @brief  Channel structure for non preferred channel
+  *
+  * @param  reason: enum non_pref_chan_reason
+  * @param  oper_class: operating class for the channel
+  * @param  chan: channel number
+  * @param  preference: channel preference
+  */
+struct non_pref_chan {
+	enum non_pref_chan_reason reason;
+	uint8_t oper_class;
+	uint8_t chan;
+	uint8_t preference;
+};
+
+/**
+  * @brief  Array structure for non preferred channel struct
+  *
+  * @param  non_pref_chan_num: channel count
+  * @param  chan: array of non_pref_chan type
+  */
+struct non_pref_chan_s {
+	size_t non_pref_chan_num;
+	struct non_pref_chan chan[];
+};
+
+/**
+  * @brief  Update channel preference for MBO IE
+  *
+  * @param  non_pref_chan: Non preference channel list
+  *
+  * @return
+  *    - 0: success else failure
+  */
+int esp_mbo_update_non_pref_chan(struct non_pref_chan_s *non_pref_chan);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 10 - 18
components/wpa_supplicant/esp_supplicant/include/esp_wnm.h

@@ -1,17 +1,7 @@
-/**
- * Copyright 2020 Espressif Systems (Shanghai) PTE LTD
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
+/*
+ * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * SPDX-License-Identifier: Apache-2.0
  */
 
 #ifndef _ESP_WNM_H
@@ -29,11 +19,13 @@ enum btm_query_reason {
 	REASON_UNSPECIFIED = 0,
 	REASON_FRAME_LOSS = 1,
 	REASON_DELAY = 2,
-	REASON_QOS_CAPACITY = 3,
-	REASON_FIRST_ASSOC = 4,
-	REASON_LOAD_BALALNCE = 5,
-	REASON_BETTER_AP = 6,
-	REASON_CURRENT_DEAUTH = 7,
+	REASON_BANDWIDTH = 3,
+	REASON_LOAD_BALANCE = 4,
+	REASON_RSSI = 5,
+	REASON_RETRANSMISSIONS = 6,
+	REASON_INTERFERENCE = 7,
+	REASON_GRAY_ZONE = 8,
+	REASON_PREMIUM_AP = 9,
 };
 
 /**

+ 235 - 57
components/wpa_supplicant/esp_supplicant/src/esp_common.c

@@ -1,17 +1,7 @@
-/**
- * Copyright 2020 Espressif Systems (Shanghai) PTE LTD
+/*
+ * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * SPDX-License-Identifier: Apache-2.0
  */
 
 #include "utils/includes.h"
@@ -37,8 +27,8 @@ static void *s_supplicant_task_hdl = NULL;
 static void *s_supplicant_evt_queue = NULL;
 static void *s_supplicant_api_lock = NULL;
 
-static int esp_handle_action_frm(u8 *frame, size_t len,
-				 u8 *sender, u32 rssi, u8 channel)
+static int handle_action_frm(u8 *frame, size_t len,
+			     u8 *sender, u32 rssi, u8 channel)
 {
 	struct ieee_mgmt_frame *frm = os_malloc(sizeof(struct ieee_mgmt_frame) + len);
 
@@ -61,7 +51,7 @@ static int esp_handle_action_frm(u8 *frame, size_t len,
 	return 0;
 }
 
-static void esp_rx_rrm_frame(struct wpa_supplicant *wpa_s, u8 *sender,
+static void handle_rrm_frame(struct wpa_supplicant *wpa_s, u8 *sender,
 			     u8 *payload, size_t len, u32 rssi)
 {
 	if (payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
@@ -78,7 +68,7 @@ static void esp_rx_rrm_frame(struct wpa_supplicant *wpa_s, u8 *sender,
 	}
 }
 
-static int esp_mgmt_rx_action(u8 *sender, u8 *payload, size_t len, u8 channel, u32 rssi)
+static int mgmt_rx_action(u8 *sender, u8 *payload, size_t len, u8 channel, u32 rssi)
 {
 	u8 category;
 	u8 bssid[ETH_ALEN];
@@ -95,13 +85,13 @@ static int esp_mgmt_rx_action(u8 *sender, u8 *payload, size_t len, u8 channel, u
 	if (category == WLAN_ACTION_WNM) {
 		ieee802_11_rx_wnm_action(wpa_s, sender, payload, len);
 	} else if (category == WLAN_ACTION_RADIO_MEASUREMENT) {
-		esp_rx_rrm_frame(wpa_s, sender, payload, len, rssi);
+		handle_rrm_frame(wpa_s, sender, payload, len, rssi);
 	}
 
 	return 0;
 }
 
-static void esp_btm_rrm_task(void *pvParameters)
+static void btm_rrm_task(void *pvParameters)
 {
 	supplicant_event_t *evt;
 	bool task_del = false;
@@ -120,7 +110,7 @@ static void esp_btm_rrm_task(void *pvParameters)
 		case SIG_SUPPLICANT_RX_ACTION:
 		{
 			struct ieee_mgmt_frame *frm = (struct ieee_mgmt_frame *)evt->data;
-			esp_mgmt_rx_action(frm->sender, frm->payload, frm->len, frm->channel, frm->rssi);
+			mgmt_rx_action(frm->sender, frm->payload, frm->len, frm->channel, frm->rssi);
 			os_free(frm);
 			break;
 		}
@@ -153,7 +143,7 @@ static void esp_btm_rrm_task(void *pvParameters)
 	vTaskDelete(NULL);
 }
 
-static void esp_clear_bssid_flag(struct wpa_supplicant *wpa_s)
+static void clear_bssid_flag(struct wpa_supplicant *wpa_s)
 {
 	wifi_config_t *config;
 
@@ -175,7 +165,7 @@ static void esp_clear_bssid_flag(struct wpa_supplicant *wpa_s)
 	wpa_printf(MSG_DEBUG, "cleared bssid flag");
 }
 
-static void esp_register_action_frame(struct wpa_supplicant *wpa_s)
+static void register_action_frame(struct wpa_supplicant *wpa_s)
 {
 	wpa_s->type &= ~(1 << WLAN_FC_STYPE_ACTION);
 	/* subtype is defined only for action frame */
@@ -193,8 +183,8 @@ static void esp_register_action_frame(struct wpa_supplicant *wpa_s)
 	esp_wifi_register_mgmt_frame_internal(wpa_s->type, wpa_s->subtype);
 }
 
-static void esp_supplicant_sta_conn_handler(void* arg, esp_event_base_t event_base,
-					    int32_t event_id, void* event_data)
+static void supplicant_sta_conn_handler(void* arg, esp_event_base_t event_base,
+					int32_t event_id, void* event_data)
 {
 	u8 bssid[ETH_ALEN];
 	u8 *ie;
@@ -215,13 +205,13 @@ static void esp_supplicant_sta_conn_handler(void* arg, esp_event_base_t event_ba
 	ieee802_11_parse_elems(wpa_s, ie, bss->ie_len);
 	wpa_bss_flush(wpa_s);
 	/* Register for action frames */
-	esp_register_action_frame(wpa_s);
+	register_action_frame(wpa_s);
 	/* clear set bssid flag */
-	esp_clear_bssid_flag(wpa_s);
+	clear_bssid_flag(wpa_s);
 }
 
-static void esp_supplicant_sta_disconn_handler(void* arg, esp_event_base_t event_base,
-						int32_t event_id, void* event_data)
+static void supplicant_sta_disconn_handler(void* arg, esp_event_base_t event_base,
+					   int32_t event_id, void* event_data)
 {
 	struct wpa_supplicant *wpa_s = &g_wpa_supp;
 	wpas_rrm_reset(wpa_s);
@@ -230,12 +220,49 @@ static void esp_supplicant_sta_disconn_handler(void* arg, esp_event_base_t event
 	}
 }
 
+static int ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender,
+				   u32 rssi, u8 channel, u64 current_tsf)
+{
+	if (type == WLAN_FC_STYPE_BEACON || type == WLAN_FC_STYPE_PROBE_RESP) {
+		return esp_handle_beacon_probe(type, frame, len, sender, rssi, channel, current_tsf);
+	} else if (type ==  WLAN_FC_STYPE_ACTION) {
+		return handle_action_frm(frame, len, sender, rssi, channel);
+	}
+
+	return -1;
+}
+
+#ifdef CONFIG_MBO
+static bool bss_profile_match(u8 *sender)
+{
+	/* Incase supplicant wants drivers to skip this BSS, return false */
+	struct wpa_bss *bss = wpa_bss_get_bssid(&g_wpa_supp, sender);
+	if (!bss) {
+		return true;
+	}
+	const u8 *assoc_disallow = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_ASSOC_DISALLOW);
+	if (assoc_disallow && assoc_disallow[1] >= 1) {
+		wpa_printf(MSG_DEBUG,
+				"skip - MBO association disallowed (reason %u)", assoc_disallow[2]);
+		return false;
+	}
+
+	if (wpa_is_bss_tmp_disallowed(&g_wpa_supp, bss)) {
+		wpa_printf(MSG_DEBUG,
+				"skip - BSS is temporary disallowed");
+		return false;
+	}
+
+	return true;
+}
+#endif
+
 void esp_supplicant_common_init(struct wpa_funcs *wpa_cb)
 {
 	struct wpa_supplicant *wpa_s = &g_wpa_supp;
 
 	s_supplicant_evt_queue = xQueueCreate(3, sizeof(supplicant_event_t));
-	xTaskCreate(esp_btm_rrm_task, "btm_rrm_t", SUPPLICANT_TASK_STACK_SIZE, NULL, 2, s_supplicant_task_hdl);
+	xTaskCreate(btm_rrm_task, "btm_rrm_t", SUPPLICANT_TASK_STACK_SIZE, NULL, 2, s_supplicant_task_hdl);
 
 	s_supplicant_api_lock = xSemaphoreCreateRecursiveMutex();
 	if (!s_supplicant_api_lock) {
@@ -248,13 +275,22 @@ void esp_supplicant_common_init(struct wpa_funcs *wpa_cb)
 	wpas_clear_beacon_rep_data(wpa_s);
 
 	esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED,
-			&esp_supplicant_sta_conn_handler, NULL);
+			&supplicant_sta_conn_handler, NULL);
 	esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED,
-			&esp_supplicant_sta_disconn_handler, NULL);
+			&supplicant_sta_disconn_handler, NULL);
 
 	wpa_s->type = 0;
 	wpa_s->subtype = 0;
-	wpa_cb->wpa_sta_rx_mgmt = esp_ieee80211_handle_rx_frm;
+	wpa_s->type |= (1 << WLAN_FC_STYPE_BEACON) | (1 << WLAN_FC_STYPE_PROBE_RESP);
+	esp_wifi_register_mgmt_frame_internal(wpa_s->type, wpa_s->subtype);
+	wpa_cb->wpa_sta_rx_mgmt = ieee80211_handle_rx_frm;
+	/* Matching is done only for MBO at the moment, this can be extended for other features*/
+#ifdef CONFIG_MBO
+	wpa_cb->wpa_sta_profile_match = bss_profile_match;
+	dl_list_init(&wpa_s->bss_tmp_disallowed);
+#else
+	wpa_cb->wpa_sta_profile_match = NULL;
+#endif
 }
 
 void esp_supplicant_common_deinit(void)
@@ -268,28 +304,38 @@ void esp_supplicant_common_deinit(void)
 	wpas_rrm_reset(wpa_s);
 	wpas_clear_beacon_rep_data(wpa_s);
 	esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED,
-			&esp_supplicant_sta_conn_handler);
+			&supplicant_sta_conn_handler);
 	esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED,
-			&esp_supplicant_sta_disconn_handler);
+			&supplicant_sta_disconn_handler);
 }
 
 int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb,
 				      void *cb_ctx)
 {
-	struct wpa_supplicant *wpa_s = &g_wpa_supp;
 	struct wpa_ssid_value wpa_ssid = {0};
 	struct wifi_ssid *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, cb, cb_ctx);
+
+	return wpas_rrm_send_neighbor_rep_request(&g_wpa_supp, &wpa_ssid, 0, 0, cb, cb_ctx);
 }
 
 int esp_wnm_send_bss_transition_mgmt_query(enum btm_query_reason query_reason,
 					   const char *btm_candidates,
 					   int cand_list)
 {
-	struct wpa_supplicant *wpa_s = &g_wpa_supp;
-	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, btm_candidates, cand_list);
+	return wnm_send_bss_transition_mgmt_query(&g_wpa_supp, query_reason, btm_candidates, cand_list);
+}
+
+int esp_mbo_update_non_pref_chan(struct non_pref_chan_s *non_pref_chan)
+{
+	int ret = wpas_mbo_update_non_pref_chan(&g_wpa_supp, non_pref_chan);
+	if (ret == 0) {
+		esp_set_assoc_ie();
+	}
+
+	return ret;
 }
 
 void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
@@ -306,18 +352,25 @@ 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;
-	esp_wifi_internal_issue_disconnect(WIFI_REASON_ROAMING);
+	/* 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);
 	os_free(config);
 	esp_wifi_connect();
 }
 
-void esp_set_rm_enabled_ie(void)
+static size_t get_rm_enabled_ie(uint8_t *ie, size_t len)
 {
-	uint8_t rmm_ie[5] = {0};
+	uint8_t rrm_ie[7] = {0};
 	uint8_t rrm_ie_len = 5;
-	uint8_t *pos = rmm_ie;
+	uint8_t *pos = rrm_ie;
+
+	if (!esp_wifi_is_rm_enabled_internal(WIFI_IF_STA)) {
+		return 0;
+	}
 
+	*pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+	*pos++ = rrm_ie_len;
 	*pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
 
 	*pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
@@ -326,10 +379,147 @@ void esp_set_rm_enabled_ie(void)
 #endif
 		WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE;
 
-	/* set rm enabled IE if enabled in driver */
-	if (esp_wifi_is_rm_enabled_internal(WIFI_IF_STA)) {
-		esp_wifi_set_appie_internal(WIFI_APPIE_RM_ENABLED_CAPS, rmm_ie, rrm_ie_len, 0);
+	os_memcpy(ie, rrm_ie, sizeof(rrm_ie));
+
+	return rrm_ie_len + 2;
+}
+
+#ifdef CONFIG_MBO
+static size_t get_mbo_oce_scan_ie(uint8_t *ie, size_t len)
+{
+	uint8_t mbo_ie[32] = {0};
+	uint8_t mbo_ie_len = 32;
+
+	/* Return if MBO IE is not enabled in driver */
+	if (!esp_wifi_is_mbo_enabled_internal(WIFI_IF_STA)) {
+		return 0;
+	}
+
+	struct wpabuf *default_ies = NULL;
+	if (wpabuf_resize(&default_ies, 18) == 0) {
+		wpas_mbo_scan_ie(&g_wpa_supp, default_ies);
+		os_memcpy(mbo_ie, wpabuf_head_u8(default_ies), wpabuf_len(default_ies));
+		mbo_ie_len = wpabuf_len(default_ies);
+		wpabuf_free(default_ies);
 	}
+	os_memcpy(ie, mbo_ie, mbo_ie_len);
+	return mbo_ie_len;
+}
+
+static size_t get_mbo_oce_assoc_ie(uint8_t *ie, size_t len)
+{
+	uint8_t mbo_ie[32] = {0};
+	uint8_t mbo_ie_len = 32;
+
+	/* Return if MBO IE is not enabled in driver */
+	if (!esp_wifi_is_mbo_enabled_internal(WIFI_IF_STA)) {
+		return 0;
+	}
+
+	mbo_ie_len = wpas_mbo_ie(&g_wpa_supp, mbo_ie, mbo_ie_len, 0);
+	os_memcpy(ie, mbo_ie, mbo_ie_len);
+
+	return mbo_ie_len;
+}
+#endif
+
+static uint8_t get_extended_caps_ie(uint8_t *ie, size_t len)
+{
+	uint8_t ext_caps_ie[5] = {0};
+	uint8_t ext_caps_ie_len = 3;
+	uint8_t *pos = ext_caps_ie;
+
+	if (!esp_wifi_is_btm_enabled_internal(WIFI_IF_STA)) {
+		return 0;
+	}
+
+	*pos++ = WLAN_EID_EXT_CAPAB;
+	*pos++ = ext_caps_ie_len;
+	*pos++ = 0;
+	*pos++ = 0;
+#define WLAN_EXT_CAPAB_BSS_TRANSITION BIT(3)
+	*pos |= WLAN_EXT_CAPAB_BSS_TRANSITION;
+#undef WLAN_EXT_CAPAB_BSS_TRANSITION
+	os_memcpy(ie, ext_caps_ie, sizeof(ext_caps_ie));
+
+	return ext_caps_ie_len + 2;
+}
+
+
+static uint8_t get_operating_class_ie(uint8_t *ie, size_t len)
+{
+	uint8_t op_class_ie[4] = {0};
+	uint8_t op_class_ie_len = 2;
+	uint8_t *pos = op_class_ie;
+
+	*pos++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES;
+	*pos++ = op_class_ie_len;
+#define OPER_CLASS 0x51
+        /* Current Operating Class */
+        *pos++ = OPER_CLASS;
+#undef OPER_CLASS
+        *pos = 0;
+	os_memcpy(ie, op_class_ie, sizeof(op_class_ie));
+
+	return op_class_ie_len + 2;
+}
+
+void esp_set_scan_ie(void)
+{
+#define SCAN_IE_LEN 64
+	uint8_t *ie, *pos;
+	size_t len = SCAN_IE_LEN, ie_len;
+
+	ie = os_malloc(SCAN_IE_LEN);
+	if (!ie) {
+		wpa_printf(MSG_ERROR, "failed to allocate ie");
+		return;
+	}
+	pos = ie;
+	ie_len = get_extended_caps_ie(pos, len);
+	pos += ie_len;
+	len -= ie_len;
+#ifdef CONFIG_MBO
+	ie_len = get_mbo_oce_scan_ie(pos, len);
+	pos += ie_len;
+	len -= ie_len;
+#endif
+	esp_wifi_unset_appie_internal(WIFI_APPIE_PROBEREQ);
+	esp_wifi_set_appie_internal(WIFI_APPIE_PROBEREQ, ie, SCAN_IE_LEN - len, 0);
+	os_free(ie);
+#undef SCAN_IE_LEN
+}
+
+void esp_set_assoc_ie(void)
+{
+#define ASSOC_IE_LEN 128
+	uint8_t *ie, *pos;
+	size_t len = ASSOC_IE_LEN, ie_len;
+
+	ie = os_malloc(ASSOC_IE_LEN);
+	if (!ie) {
+		wpa_printf(MSG_ERROR, "failed to allocate ie");
+		return;
+	}
+	pos = ie;
+	ie_len = get_extended_caps_ie(pos, len);
+	pos += ie_len;
+	len -= ie_len;
+	ie_len = get_operating_class_ie(pos, len);
+	pos += ie_len;
+	len -= ie_len;
+	ie_len = get_rm_enabled_ie(pos, len);
+	pos += ie_len;
+	len -= ie_len;
+#ifdef CONFIG_MBO
+	ie_len = get_mbo_oce_assoc_ie(pos, len);
+	pos += ie_len;
+	len -= ie_len;
+#endif
+	esp_wifi_unset_appie_internal(WIFI_APPIE_ASSOC_REQ);
+	esp_wifi_set_appie_internal(WIFI_APPIE_ASSOC_REQ, ie, ASSOC_IE_LEN - len, 0);
+	os_free(ie);
+#undef ASSOC_IE_LEN
 }
 
 void esp_get_tx_power(uint8_t *tx_power)
@@ -399,15 +589,3 @@ int esp_supplicant_post_evt(uint32_t evt_id, uint32_t data)
 	SUPPLICANT_API_UNLOCK();
 	return 0;
 }
-
-int esp_ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender,
-				u32 rssi, u8 channel, u64 current_tsf)
-{
-	if (type == WLAN_FC_STYPE_BEACON || type == WLAN_FC_STYPE_PROBE_RESP) {
-		return esp_handle_beacon_probe(type, frame, len, sender, rssi, channel, current_tsf);
-	} else if (type ==  WLAN_FC_STYPE_ACTION) {
-		return esp_handle_action_frm(frame, len, sender, rssi, channel);
-	}
-
-	return -1;
-}

+ 13 - 22
components/wpa_supplicant/esp_supplicant/src/esp_common_i.h

@@ -1,17 +1,7 @@
-/**
- * Copyright 2020 Espressif Systems (Shanghai) PTE LTD
+/*
+ * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * SPDX-License-Identifier: Apache-2.0
  */
 
 #ifndef ESP_COMMON_I_H
@@ -47,23 +37,20 @@ enum SIG_SUPPLICANT {
 };
 
 int esp_supplicant_post_evt(uint32_t evt_id, uint32_t data);
-int esp_ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender,
-			    u32 rssi, u8 channel, u64 current_tsf);
-void esp_set_rm_enabled_ie(void);
 void esp_get_tx_power(uint8_t *tx_power);
 void esp_supplicant_common_init(struct wpa_funcs *wpa_cb);
 void esp_supplicant_common_deinit(void);
+void esp_set_scan_ie(void);
+void esp_set_assoc_ie(void);
 #else
 
 #include "esp_rrm.h"
 #include "esp_wnm.h"
+#include "esp_mbo.h"
+
+static inline void esp_set_scan_ie(void) { }
+static inline void esp_set_assoc_ie(void) { }
 
-static inline void esp_set_rm_enabled_ie(void) {}
-static inline int esp_ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender,
-			    u32 rssi, u8 channel, u64 current_tsf)
-{
-	return -1;
-}
 int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb,
 				      void *cb_ctx)
 {
@@ -77,5 +64,9 @@ int esp_wnm_send_bss_transition_mgmt_query(enum btm_query_reason query_reason,
 	return -1;
 }
 
+int esp_mbo_update_non_pref_chan(struct non_pref_chan_s *non_pref_chan)
+{
+	return -1;
+}
 #endif
 #endif

+ 20 - 29
components/wpa_supplicant/esp_supplicant/src/esp_scan.c

@@ -1,17 +1,7 @@
-/**
- * Copyright 2020 Espressif Systems (Shanghai) PTE LTD
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+/*
+ * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * SPDX-License-Identifier: Apache-2.0
  */
 
 #include "utils/includes.h"
@@ -32,8 +22,8 @@
 
 extern struct wpa_supplicant g_wpa_supp;
 
-static void esp_scan_done_event_handler(void* arg, esp_event_base_t event_base,
-					int32_t event_id, void* event_data)
+static void scan_done_event_handler(void* arg, esp_event_base_t event_base,
+				    int32_t event_id, void* event_data)
 {
 	struct wpa_supplicant *wpa_s = &g_wpa_supp;
 
@@ -46,7 +36,7 @@ static void esp_scan_done_event_handler(void* arg, esp_event_base_t event_base,
 	esp_supplicant_post_evt(SIG_SUPPLICANT_SCAN_DONE, 0);
 }
 
-static void esp_supp_handle_wnm_scan_done(struct wpa_supplicant *wpa_s)
+static void handle_wnm_scan_done(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_bss *bss = wpa_bss_get_next_bss(wpa_s, wpa_s->current_bss);
 
@@ -64,7 +54,7 @@ static void esp_supp_handle_wnm_scan_done(struct wpa_supplicant *wpa_s)
 	}
 }
 
-static void esp_supp_scan_done_cleanup(struct wpa_supplicant *wpa_s)
+static void scan_done_cleanup(struct wpa_supplicant *wpa_s)
 {
 	uint16_t number = 1;
 	wifi_ap_record_t ap_records;
@@ -84,10 +74,10 @@ void esp_supplicant_handle_scan_done_evt(void)
 	if (wpa_s->scan_reason == REASON_RRM_BEACON_REPORT) {
 		wpas_beacon_rep_scan_process(wpa_s, wpa_s->scan_start_tsf);
 	} else if (wpa_s->scan_reason == REASON_WNM_BSS_TRANS_REQ) {
-		esp_supp_handle_wnm_scan_done(wpa_s);
+		handle_wnm_scan_done(wpa_s);
 	}
 	if (wpa_s->scanning) {
-		esp_supp_scan_done_cleanup(wpa_s);
+		scan_done_cleanup(wpa_s);
 	}
 	wpa_bss_update_end(wpa_s);
 #ifndef SCAN_CACHE_SUPPORTED
@@ -101,7 +91,7 @@ void esp_scan_init(struct wpa_supplicant *wpa_s)
 	wpa_bss_init(wpa_s);
 	wpa_s->last_scan_res = NULL;
 	esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_SCAN_DONE,
-			&esp_scan_done_event_handler, NULL);
+			&scan_done_event_handler, NULL);
 }
 
 void esp_scan_deinit(struct wpa_supplicant *wpa_s)
@@ -110,7 +100,7 @@ void esp_scan_deinit(struct wpa_supplicant *wpa_s)
 	os_free(wpa_s->last_scan_res);
 	wpa_s->last_scan_res = NULL;
 	esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_SCAN_DONE,
-			&esp_scan_done_event_handler);
+			&scan_done_event_handler);
 }
 
 int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender,
@@ -158,10 +148,7 @@ int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender,
 	res->level = rssi;
 	os_memcpy(res->tsf_bssid, wpa_s->tsf_bssid, ETH_ALEN);
 	res->parent_tsf = current_tsf - wpa_s->scan_start_tsf;
-	if (type == WLAN_FC_STYPE_PROBE_RESP)
-		res->ie_len = len;
-	else if (type == WLAN_FC_STYPE_BEACON)
-		res->beacon_ie_len = len;
+	res->ie_len = len;
 
 	ptr += sizeof(struct wpa_scan_res);
 
@@ -173,8 +160,8 @@ int esp_handle_beacon_probe(u8 type, u8 *frame, size_t len, u8 *sender,
 	return 0;
 }
 
-static int esp_issue_scan(struct wpa_supplicant *wpa_s,
-			  struct wpa_driver_scan_params *scan_params)
+static int issue_scan(struct wpa_supplicant *wpa_s,
+		      struct wpa_driver_scan_params *scan_params)
 {
 	wifi_scan_config_t *params = NULL;
 	int ret = 0;
@@ -203,9 +190,13 @@ static int esp_issue_scan(struct wpa_supplicant *wpa_s,
 				goto cleanup;
 			}
 			os_memcpy(params->ssid, scan_params->ssids[0].ssid, scan_params->ssids[0].ssid_len);
-			params->scan_type = WIFI_SCAN_TYPE_ACTIVE;
 		} else
+
+		if (scan_params->mode == BEACON_REPORT_MODE_PASSIVE) {
 			params->scan_type = WIFI_SCAN_TYPE_PASSIVE;
+		} else {
+			params->scan_type = WIFI_SCAN_TYPE_ACTIVE;
+		}
 
 		if (scan_params->bssid) {
 			params->bssid = os_zalloc(ETH_ALEN);
@@ -260,7 +251,7 @@ cleanup:
 int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
 				struct wpa_driver_scan_params *params)
 {
-	return esp_issue_scan(wpa_s, params);
+	return issue_scan(wpa_s, params);
 }
 
 void wpa_scan_results_free(struct wpa_scan_results *res)

+ 8 - 15
components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h

@@ -1,16 +1,8 @@
-// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
 
 #ifndef _ESP_WIFI_DRIVER_H_
 #define _ESP_WIFI_DRIVER_H_
@@ -57,8 +49,7 @@ typedef enum {
 
 /* wifi_appie_t is in rom code and can't be changed anymore, use wifi_appie_ram_t for new app IEs */
 typedef enum {
-    WIFI_APPIE_RM_ENABLED_CAPS = WIFI_APPIE_MAX,
-    WIFI_APPIE_RAM_MAX,
+    WIFI_APPIE_RAM_MAX = WIFI_APPIE_MAX,
 } wifi_appie_ram_t;
 
 enum {
@@ -137,6 +128,7 @@ struct wpa_funcs {
     int (*wpa3_parse_sae_msg)(uint8_t *buf, size_t len, uint32_t type, uint16_t status);
     int (*wpa_sta_rx_mgmt)(u8 type, u8 *frame, size_t len, u8 *sender, u32 rssi, u8 channel, u64 current_tsf);
     void (*wpa_config_done)(void);
+    bool (*wpa_sta_profile_match)(u8 *bssid);
 };
 
 struct wpa2_funcs {
@@ -266,5 +258,6 @@ esp_err_t esp_wifi_action_tx_req(uint8_t type, uint8_t channel,
                                  uint32_t wait_time_ms, const wifi_action_tx_req_t *req);
 esp_err_t esp_wifi_remain_on_channel(uint8_t ifx, uint8_t type, uint8_t channel,
                                      uint32_t wait_time_ms, wifi_action_rx_cb_t rx_cb);
+bool esp_wifi_is_mbo_enabled_internal(uint8_t if_index);
 
 #endif /* _ESP_WIFI_DRIVER_H_ */

+ 9 - 14
components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c

@@ -1,16 +1,8 @@
-// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
 
 #include "utils/includes.h"
 #include "utils/common.h"
@@ -123,6 +115,8 @@ bool  wpa_attach(void)
     if(ret) {
         ret = (esp_wifi_register_tx_cb_internal(eapol_txcb, WIFI_TXCB_EAPOL_ID) == ESP_OK);
     }
+    esp_set_scan_ie();
+    esp_set_assoc_ie();
     return ret;
 }
 
@@ -185,7 +179,7 @@ void  wpa_sta_connect(uint8_t *bssid)
 void wpa_config_done(void)
 {
     /* used in future for setting scan and assoc IEs */
-    esp_set_rm_enabled_ie();
+    esp_set_assoc_ie();
 }
 
 int wpa_parse_wpa_ie_wrapper(const u8 *wpa_ie, size_t wpa_ie_len, wifi_wpa_ie_t *data)
@@ -230,6 +224,7 @@ static void wpa_sta_disconnected_cb(uint8_t reason_code)
 static inline void esp_supplicant_common_init(struct wpa_funcs *wpa_cb)
 {
 	wpa_cb->wpa_sta_rx_mgmt = NULL;
+	wpa_cb->wpa_sta_profile_match = NULL;
 }
 static inline void esp_supplicant_common_deinit(void)
 {

+ 30 - 4
components/wpa_supplicant/src/common/bss.c

@@ -132,8 +132,9 @@ static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 
 	return !is_zero_ether_addr(bss->bssid) && wpa_s->current_bss->bssid &&
 		(os_memcmp(bss->bssid, wpa_s->current_bss->bssid, ETH_ALEN) == 0);
-#endif
+#else
 	return 0;
+#endif
 }
 
 static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
@@ -198,7 +199,7 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
 	bss->ssid_len = ssid_len;
 	bss->ie_len = res->ie_len;
 	bss->beacon_ie_len = res->beacon_ie_len;
-	os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
+	os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
 
 	dl_list_add_tail(&wpa_s->bss, &bss->list);
 	dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
@@ -238,7 +239,7 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 	dl_list_del(&bss->list);
 	if (bss->ie_len + bss->beacon_ie_len >=
 	    res->ie_len + res->beacon_ie_len) {
-		os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
+		os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
 		bss->ie_len = res->ie_len;
 		bss->beacon_ie_len = res->beacon_ie_len;
 	} else {
@@ -258,7 +259,7 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			if (wpa_s->current_bss == bss)
 				wpa_s->current_bss = nbss;
 			bss = nbss;
-			os_memcpy(bss + 1, res + 1,
+			os_memcpy(bss->ies, res + 1,
 				  res->ie_len + res->beacon_ie_len);
 			bss->ie_len = res->ie_len;
 			bss->beacon_ie_len = res->beacon_ie_len;
@@ -471,6 +472,31 @@ const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
 	return get_ie((const u8 *) (bss + 1), bss->ie_len, ie);
 }
 
+/**
+ * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ */
+const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
+{
+	const u8 *ies;
+	const struct element *elem;
+
+	ies = wpa_bss_ie_ptr(bss);
+
+	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, bss->ie_len) {
+		if (elem->datalen >= 4 &&
+				vendor_type == WPA_GET_BE32(elem->data))
+			return &elem->id;
+	}
+
+	return NULL;
+}
+
 int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab)
 {
 	return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB),

+ 7 - 0
components/wpa_supplicant/src/common/bss.h

@@ -58,8 +58,14 @@ struct wpa_bss {
 	size_t beacon_ie_len;
 	/* followed by ie_len octets of IEs */
 	/* followed by beacon_ie_len octets of IEs */
+	u8 ies[];
 };
 
+static inline const u8 * wpa_bss_ie_ptr(const struct wpa_bss *bss)
+{
+	return bss->ies;
+}
+
 void wpa_bss_update_start(struct wpa_supplicant *wpa_s);
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_res *res,
@@ -75,6 +81,7 @@ struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
 struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
 				   const u8 *bssid);
 const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
+const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
 int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab);
 struct wpa_bss * wpa_bss_get_next_bss(struct wpa_supplicant *wpa_s,
 				   struct wpa_bss *prev_bss);

+ 36 - 0
components/wpa_supplicant/src/common/ieee802_11_common.c

@@ -37,6 +37,42 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
 	return NULL;
 }
 
+const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
+{
+	const struct element *elem;
+
+	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
+		if (elem->datalen >= 4 &&
+				vendor_type == WPA_GET_BE32(elem->data))
+			return &elem->id;
+	}
+
+	return NULL;
+}
+
+size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
+{
+	/*
+	 * MBO IE requires 6 bytes without the attributes: EID (1), length (1),
+	 * OUI (3), OUI type (1).
+	 */
+	if (len < 6 + attr_len) {
+		wpa_printf(MSG_DEBUG,
+			   "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
+			   len, attr_len);
+		return 0;
+	}
+
+	*buf++ = WLAN_EID_VENDOR_SPECIFIC;
+	*buf++ = attr_len + 4;
+	WPA_PUT_BE24(buf, OUI_WFA);
+	buf += 3;
+	*buf++ = MBO_OUI_TYPE;
+	os_memcpy(buf, attr, attr_len);
+
+	return 6 + attr_len;
+}
+
 int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
 				    size_t nei_rep_len)
 {

+ 2 - 0
components/wpa_supplicant/src/common/ieee802_11_common.h

@@ -39,5 +39,7 @@ int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
 const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
 int ieee802_11_parse_elems(struct wpa_supplicant *wpa_s, const u8 *start, size_t len);
 int ieee802_11_ext_capab(const u8 *ie, unsigned int capab);
+const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type);
+size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
 u8 get_operating_class(u8 chan, int sec_channel);
 #endif /* IEEE802_11_COMMON_H */

+ 53 - 0
components/wpa_supplicant/src/common/ieee802_11_defs.h

@@ -212,6 +212,7 @@
 #define WLAN_EID_FAST_BSS_TRANSITION 55
 #define WLAN_EID_TIMEOUT_INTERVAL 56
 #define WLAN_EID_RIC_DATA 57
+#define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
 #define WLAN_EID_HT_OPERATION 61
 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
 #define WLAN_EID_WAPI 68
@@ -536,6 +537,13 @@ struct ieee80211_ht_operation {
 /* 2 - Reserved */
 #define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3
 
+#define MBO_IE_VENDOR_TYPE 0x506f9a16
+#define OSEN_IE_VENDOR_TYPE 0x506f9a12
+#define MBO_OUI_TYPE 22
+#define OCE_STA BIT(0)
+#define OCE_STA_CFON BIT(1)
+#define OCE_AP BIT(2)
+
 /*
  * WMM Information Element (used in (Re)Association Request frames; may also be
  * used in Beacon frames)
@@ -622,6 +630,45 @@ enum wmm_ac {
 	WMM_AC_NUM = 4
 };
 
+/* MBO v0.0_r19, 4.2: MBO Attributes */
+/* Table 4-5: MBO Attributes */
+/* OCE v0.0.10, Table 4-3: OCE Attributes */
+enum mbo_attr_id {
+	MBO_ATTR_ID_AP_CAPA_IND = 1,
+	MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2,
+	MBO_ATTR_ID_CELL_DATA_CAPA = 3,
+	MBO_ATTR_ID_ASSOC_DISALLOW = 4,
+	MBO_ATTR_ID_CELL_DATA_PREF = 5,
+	MBO_ATTR_ID_TRANSITION_REASON = 6,
+	MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7,
+	MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8,
+	OCE_ATTR_ID_CAPA_IND = 101,
+	OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT = 102,
+	OCE_ATTR_ID_REDUCED_WAN_METRICS = 103,
+	OCE_ATTR_ID_RNR_COMPLETENESS = 104,
+};
+
+/* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */
+/* Table 4-7: MBO AP Capability Indication Field Values */
+#define MBO_AP_CAPA_CELL_AWARE BIT(6)
+
+/* MBO v0.0_r19, 4.2.2: Non-preferred Channel Report Attribute */
+/* Table 4-10: Reason Code Field Values */
+enum mbo_non_pref_chan_reason {
+	MBO_NON_PREF_CHAN_REASON_UNSPECIFIED = 0,
+	MBO_NON_PREF_CHAN_REASON_RSSI = 1,
+	MBO_NON_PREF_CHAN_REASON_EXT_INTERFERENCE = 2,
+	MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE = 3,
+};
+
+/* MBO v0.0_r19, 4.2.3: Cellular Data Capabilities Attribute */
+/* Table 4-13: Cellular Data Connectivity Field */
+enum mbo_cellular_capa {
+	MBO_CELL_CAPA_AVAILABLE = 1,
+	MBO_CELL_CAPA_NOT_AVAILABLE = 2,
+	MBO_CELL_CAPA_NOT_SUPPORTED = 3,
+};
+
 /* MBO v0.0_r19, 4.2.7: Transition Rejection Reason Code Attribute */
 /* Table 4-21: Transition Rejection Reason Code Field Values */
 enum mbo_transition_reject_reason {
@@ -634,6 +681,12 @@ enum mbo_transition_reject_reason {
 	MBO_TRANSITION_REJECT_REASON_SERVICES = 6,
 };
 
+/* OCE v0.0.10, 4.2.1: OCE Capability Indication Attribute */
+#define OCE_RELEASE 1
+#define OCE_RELEASE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define OCE_IS_STA_CFON BIT(3)
+#define OCE_IS_NON_OCE_AP_PRESENT BIT(4)
+
 /* IEEE 802.11v - WNM Action field values */
 enum wnm_action {
 	WNM_EVENT_REQ = 0,

+ 799 - 0
components/wpa_supplicant/src/common/mbo.c

@@ -0,0 +1,799 @@
+/*
+ * wpa_supplicant - MBO
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "bss.h"
+#include "scan.h"
+#include "ieee802_11_common.h"
+
+#ifdef ESP_SUPPLICANT
+#include "esp_timer.h"
+#include "esp_mbo.h"
+
+extern struct wpa_supplicant g_wpa_supp;
+
+#endif
+
+/* type + length + oui + oui type */
+#define MBO_IE_HEADER 6
+
+#ifndef ESP_SUPPLICANT
+static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason)
+{
+	if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE)
+		return -1;
+
+	/* Only checking the validity of the channel and oper_class */
+	if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1)
+		return -1;
+
+	return 0;
+}
+#endif
+
+const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr)
+{
+	const u8 *mbo;
+	u8 ie_len = mbo_ie[1];
+
+	if (ie_len < MBO_IE_HEADER - 2)
+		return NULL;
+	mbo = mbo_ie + MBO_IE_HEADER;
+
+	return get_ie(mbo, 2 + ie_len - MBO_IE_HEADER, attr);
+}
+
+
+const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len,
+				 enum mbo_attr_id attr)
+{
+	const u8 *mbo_ie;
+
+	mbo_ie = get_vendor_ie(ies, ies_len, MBO_IE_VENDOR_TYPE);
+	if (!mbo_ie)
+		return NULL;
+
+	return mbo_attr_from_mbo_ie(mbo_ie, attr);
+}
+
+
+const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr)
+{
+	const u8 *mbo, *end;
+
+	if (!bss)
+		return NULL;
+
+	mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+	if (!mbo)
+		return NULL;
+
+	end = mbo + 2 + mbo[1];
+	mbo += MBO_IE_HEADER;
+
+	return get_ie(mbo, end - mbo, attr);
+}
+
+
+void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss
+#ifndef ESP_SUPPLICANT
+			, struct wpa_ssid *ssid
+#endif
+			)
+{
+	const u8 *rsne, *mbo, *oce;
+	struct wpa_ie_data ie;
+
+	wpa_s->disable_mbo_oce = 0;
+	if (!bss)
+		return;
+	mbo = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND);
+	oce = wpas_mbo_get_bss_attr(bss, OCE_ATTR_ID_CAPA_IND);
+	if (!mbo && !oce)
+		return;
+	if (oce && oce[1] >= 1 && (oce[2] & OCE_IS_STA_CFON))
+		return; /* STA-CFON is not required to enable PMF */
+	rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	if (!rsne || wpa_parse_wpa_ie(rsne, 2 + rsne[1], &ie) < 0)
+		return; /* AP is not using RSN */
+
+	if (!(ie.capabilities & WPA_CAPABILITY_MFPC))
+		wpa_s->disable_mbo_oce = 1; /* AP uses RSN without PMF */
+#ifdef ESP_SUPPLICANT
+	if (!esp_wifi_sta_pmf_enabled())
+#else
+	if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
+#endif
+		wpa_s->disable_mbo_oce = 1; /* STA uses RSN without PMF */
+	if (wpa_s->disable_mbo_oce)
+		wpa_printf(MSG_INFO,
+			   "MBO: Disable MBO/OCE due to misbehaving AP not having enabled PMF");
+}
+
+static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s,
+					     struct wpabuf *mbo,
+					     u8 start, u8 end)
+{
+	u8 i;
+
+	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class);
+
+	for (i = start; i < end; i++)
+		wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan);
+
+	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference);
+	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason);
+}
+
+
+static void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf *mbo, size_t size)
+{
+	wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
+	wpabuf_put_u8(mbo, size); /* Length */
+}
+
+
+static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s,
+					struct wpabuf *mbo, u8 start, u8 end)
+{
+	size_t size = end - start + 3;
+
+	if (size + 2 > wpabuf_tailroom(mbo))
+		return;
+
+	wpas_mbo_non_pref_chan_attr_hdr(mbo, size);
+	wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
+}
+
+
+static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len)
+{
+	wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(mbo, len); /* Length */
+	wpabuf_put_be24(mbo, OUI_WFA);
+	wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
+}
+
+
+static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s,
+					      struct wpabuf *mbo, u8 start,
+					      u8 end)
+{
+	size_t size = end - start + 7;
+
+	if (size + 2 > wpabuf_tailroom(mbo))
+		return;
+
+	wpas_mbo_non_pref_chan_subelem_hdr(mbo, size);
+	wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
+}
+
+
+static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s,
+					 struct wpabuf *mbo, int subelement)
+{
+	u8 i, start = 0;
+	struct wpa_mbo_non_pref_channel *start_pref;
+
+	if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) {
+		if (subelement)
+			wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4);
+		else
+			wpas_mbo_non_pref_chan_attr_hdr(mbo, 0);
+		return;
+	}
+	start_pref = &wpa_s->non_pref_chan[0];
+
+	for (i = 1; i <= wpa_s->non_pref_chan_num; i++) {
+		struct wpa_mbo_non_pref_channel *non_pref = NULL;
+
+		if (i < wpa_s->non_pref_chan_num)
+			non_pref = &wpa_s->non_pref_chan[i];
+		if (!non_pref ||
+		    non_pref->oper_class != start_pref->oper_class ||
+		    non_pref->reason != start_pref->reason ||
+		    non_pref->preference != start_pref->preference) {
+			if (subelement)
+				wpas_mbo_non_pref_chan_subelement(wpa_s, mbo,
+								  start, i);
+			else
+				wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start,
+							    i);
+
+			if (!non_pref)
+				return;
+
+			start = i;
+			start_pref = non_pref;
+		}
+	}
+}
+
+int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len,
+		int add_oce_capa)
+{
+	struct wpabuf *mbo;
+	int res;
+
+	if (len < MBO_IE_HEADER + 3 + 7 +
+	    ((wpa_s->enable_oce & OCE_STA) ? 3 : 0))
+		return 0;
+
+	/* Leave room for the MBO IE header */
+	mbo = wpabuf_alloc(len - MBO_IE_HEADER);
+	if (!mbo)
+		return 0;
+
+	/* Add non-preferred channels attribute */
+	wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0);
+
+	/*
+	 * Send cellular capabilities attribute even if AP does not advertise
+	 * cellular capabilities.
+	 */
+	wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA);
+	wpabuf_put_u8(mbo, 1);
+#ifdef ESP_SUPPLICANT
+	wpabuf_put_u8(mbo, MBO_CELL_CAPA_NOT_SUPPORTED);
+#else
+	wpabuf_put_u8(mbo, wpa_s->mbo_cell_capa);
+#endif
+
+	/* Add OCE capability indication attribute if OCE is enabled */
+	if ((wpa_s->enable_oce & OCE_STA) && add_oce_capa) {
+		wpabuf_put_u8(mbo, OCE_ATTR_ID_CAPA_IND);
+		wpabuf_put_u8(mbo, 1);
+		wpabuf_put_u8(mbo, OCE_RELEASE);
+	}
+
+	res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
+	if (!res)
+		wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE");
+
+	wpabuf_free(mbo);
+	return res;
+}
+
+
+static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s,
+					   const u8 *data, size_t len)
+{
+	struct wpabuf *buf;
+	int res;
+
+	/*
+	 * Send WNM-Notification Request frame only in case of a change in
+	 * non-preferred channels list during association, if the AP supports
+	 * MBO.
+	 */
+	if (!wpa_s->current_bss ||
+	    !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE))
+		return;
+
+	buf = wpabuf_alloc(4 + len);
+	if (!buf)
+		return;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+	wpa_s->mbo_wnm_token++;
+	if (wpa_s->mbo_wnm_token == 0)
+		wpa_s->mbo_wnm_token++;
+	wpabuf_put_u8(buf, wpa_s->mbo_wnm_token);
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */
+
+	wpabuf_put_data(buf, data, len);
+
+	res = wpa_drv_send_action(wpa_s, 0, 0,
+				  wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (res < 0)
+		wpa_printf(MSG_DEBUG,
+			   "Failed to send WNM-Notification Request frame with non-preferred channel list");
+
+	wpabuf_free(buf);
+}
+
+
+static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(512);
+	if (!buf)
+		return;
+
+	wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1);
+	wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf),
+				       wpabuf_len(buf));
+	wpabuf_free(buf);
+}
+
+
+#ifndef ESP_SUPPLICANT
+static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a,
+				   struct wpa_mbo_non_pref_channel *b)
+{
+	return a->oper_class == b->oper_class && a->chan == b->chan;
+}
+
+/*
+ * wpa_non_pref_chan_cmp - Compare two channels for sorting
+ *
+ * In MBO IE non-preferred channel subelement we can put many channels in an
+ * attribute if they are in the same operating class and have the same
+ * preference and reason. To make it easy for the functions that build
+ * the IE attributes and WNM Request subelements, save the channels sorted
+ * by their oper_class and reason.
+ */
+static int wpa_non_pref_chan_cmp(const void *_a, const void *_b)
+{
+	const struct wpa_mbo_non_pref_channel *a = _a, *b = _b;
+
+	if (a->oper_class != b->oper_class)
+		return (int) a->oper_class - (int) b->oper_class;
+	if (a->reason != b->reason)
+		return (int) a->reason - (int) b->reason;
+	return (int) a->preference - (int) b->preference;
+}
+
+
+int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
+				  const char *non_pref_chan)
+{
+	char *cmd, *token, *context = NULL;
+	struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans;
+	size_t num = 0, size = 0;
+	unsigned i;
+
+	wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s",
+		   non_pref_chan ? non_pref_chan : "N/A");
+
+	/*
+	 * The shortest channel configuration is 7 characters - 3 colons and
+	 * 4 values.
+	 */
+	if (!non_pref_chan || os_strlen(non_pref_chan) < 7)
+		goto update;
+
+	cmd = os_strdup(non_pref_chan);
+	if (!cmd)
+		return -1;
+
+	while ((token = str_token(cmd, " ", &context))) {
+		struct wpa_mbo_non_pref_channel *chan;
+		int ret;
+		unsigned int _oper_class;
+		unsigned int _chan;
+		unsigned int _preference;
+		unsigned int _reason;
+
+		if (num == size) {
+			size = size ? size * 2 : 1;
+			tmp_chans = os_realloc_array(chans, size,
+						     sizeof(*chans));
+			if (!tmp_chans) {
+				wpa_printf(MSG_ERROR,
+					   "Couldn't reallocate non_pref_chan");
+				goto fail;
+			}
+			chans = tmp_chans;
+		}
+
+		chan = &chans[num];
+
+		ret = sscanf(token, "%u:%u:%u:%u", &_oper_class,
+			     &_chan, &_preference, &_reason);
+		if (ret != 4 ||
+		    _oper_class > 255 || _chan > 255 ||
+		    _preference > 255 || _reason > 65535 ) {
+			wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s",
+				   token);
+			goto fail;
+		}
+		chan->oper_class = _oper_class;
+		chan->chan = _chan;
+		chan->preference = _preference;
+		chan->reason = _reason;
+
+		if (wpas_mbo_validate_non_pref_chan(chan->oper_class,
+						    chan->chan, chan->reason)) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid non_pref_chan: oper class %d chan %d reason %d",
+				   chan->oper_class, chan->chan, chan->reason);
+			goto fail;
+		}
+
+		for (i = 0; i < num; i++)
+			if (wpa_non_pref_chan_is_eq(chan, &chans[i]))
+				break;
+		if (i != num) {
+			wpa_printf(MSG_ERROR,
+				   "oper class %d chan %d is duplicated",
+				   chan->oper_class, chan->chan);
+			goto fail;
+		}
+
+		num++;
+	}
+
+	os_free(cmd);
+
+	if (chans) {
+		qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel),
+		      wpa_non_pref_chan_cmp);
+	}
+
+update:
+	os_free(wpa_s->non_pref_chan);
+	wpa_s->non_pref_chan = chans;
+	wpa_s->non_pref_chan_num = num;
+	wpas_mbo_non_pref_chan_changed(wpa_s);
+
+	return 0;
+
+fail:
+	os_free(chans);
+	os_free(cmd);
+	return -1;
+}
+#else
+int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
+				  struct non_pref_chan_s *non_pref_chan)
+{
+	struct wpa_mbo_non_pref_channel *chans = NULL;
+	/*
+	 * The shortest channel configuration is 7 characters - 3 colons and
+	 * 4 values.
+	 */
+	if (!non_pref_chan)
+		goto update;
+
+
+	chans = os_malloc(sizeof(struct wpa_mbo_non_pref_channel) * non_pref_chan->non_pref_chan_num);
+	os_memcpy(chans, non_pref_chan->chan, sizeof(struct wpa_mbo_non_pref_channel) * non_pref_chan->non_pref_chan_num);
+
+update:
+	os_free(wpa_s->non_pref_chan);
+	wpa_s->non_pref_chan = chans;
+	if (non_pref_chan)
+		wpa_s->non_pref_chan_num = non_pref_chan->non_pref_chan_num;
+	else
+		wpa_s->non_pref_chan_num = 0;
+	wpas_mbo_non_pref_chan_changed(wpa_s);
+
+	return 0;
+}
+
+#endif
+
+void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
+{
+	u8 *len;
+
+	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(ie, 1);
+
+	wpabuf_put_be24(ie, OUI_WFA);
+	wpabuf_put_u8(ie, MBO_OUI_TYPE);
+
+	wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA);
+	wpabuf_put_u8(ie, 1);
+#ifdef ESP_SUPPLICANT
+	wpabuf_put_u8(ie, MBO_CELL_CAPA_NOT_SUPPORTED);
+#else
+	wpabuf_put_u8(ie, wpa_s->mbo_cell_capa);
+#endif
+	if (wpa_s->enable_oce & OCE_STA) {
+		wpabuf_put_u8(ie, OCE_ATTR_ID_CAPA_IND);
+		wpabuf_put_u8(ie, 1);
+		wpabuf_put_u8(ie, OCE_RELEASE);
+	}
+	*len = (u8 *) wpabuf_put(ie, 0) - len - 1;
+}
+
+#ifdef ESP_SUPPLICANT
+
+static struct
+wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
+						 const u8 *bssid)
+{
+	struct wpa_bss_tmp_disallowed *bss;
+
+	dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
+			 struct wpa_bss_tmp_disallowed, list) {
+		if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
+			return bss;
+	}
+
+	return NULL;
+}
+
+static void wpa_bss_tmp_disallow_timeout(void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = &g_wpa_supp;
+	struct wpa_bss_tmp_disallowed *tmp, *bss = timeout_ctx;
+
+	/* Make sure the bss is not already freed */
+	dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed,
+			 struct wpa_bss_tmp_disallowed, list) {
+		if (bss == tmp) {
+			dl_list_del(&tmp->list);
+			esp_timer_stop(bss->blacklist_timer);
+			esp_timer_delete(bss->blacklist_timer);
+			os_free(tmp);
+			break;
+		}
+	}
+}
+
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			  unsigned int sec, int rssi_threshold)
+{
+	struct wpa_bss_tmp_disallowed *bss;
+
+	bss = wpas_get_disallowed_bss(wpa_s, bssid);
+	if (bss) {
+		esp_timer_stop(bss->blacklist_timer);
+		goto finish;
+	}
+
+	bss = os_malloc(sizeof(*bss));
+	esp_timer_create_args_t blacklist_timer_create = {
+		.callback = &wpa_bss_tmp_disallow_timeout,
+		.arg = bss,
+		.dispatch_method = ESP_TIMER_TASK,
+		.name = "blacklist_timeout_timer"
+	};
+	esp_timer_create(&blacklist_timer_create, &(bss->blacklist_timer));
+
+	if (!bss) {
+		wpa_printf(MSG_DEBUG,
+				"Failed to allocate memory for temp disallow BSS");
+		return;
+	}
+
+	os_memcpy(bss->bssid, bssid, ETH_ALEN);
+	dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
+
+finish:
+	esp_timer_start_once(bss->blacklist_timer, (sec + 1) * 1e6);
+}
+
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s,
+			      struct wpa_bss *bss)
+{
+	struct wpa_bss_tmp_disallowed *disallowed = NULL, *tmp, *prev;
+
+	dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
+			      struct wpa_bss_tmp_disallowed, list) {
+		if (os_memcmp(bss->bssid, tmp->bssid, ETH_ALEN) == 0) {
+			disallowed = tmp;
+			break;
+		}
+	}
+	if (!disallowed)
+		return 0;
+
+	return 1;
+}
+#endif
+
+void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
+			   size_t len)
+{
+	const u8 *pos, *cell_pref = NULL;
+	u8 id, elen;
+	u16 disallowed_sec = 0;
+
+	if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
+	    mbo_ie[3] != MBO_OUI_TYPE)
+		return;
+
+	pos = mbo_ie + 4;
+	len -= 4;
+
+	while (len >= 2) {
+		id = *pos++;
+		elen = *pos++;
+		len -= 2;
+
+		if (elen > len)
+			goto fail;
+
+		switch (id) {
+		case MBO_ATTR_ID_CELL_DATA_PREF:
+			if (elen != 1)
+				goto fail;
+
+#ifndef ESP_SUPPLICANT
+			if (wpa_s->mbo_cell_capa ==
+			    MBO_CELL_CAPA_AVAILABLE)
+				cell_pref = pos;
+			else
+#endif
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Station does not support Cellular data connection");
+			break;
+		case MBO_ATTR_ID_TRANSITION_REASON:
+			if (elen != 1)
+				goto fail;
+
+			wpa_s->wnm_mbo_trans_reason_present = 1;
+			wpa_s->wnm_mbo_transition_reason = *pos;
+			break;
+		case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
+			if (elen != 2)
+				goto fail;
+
+			if (wpa_s->wnm_mode &
+			    WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Unexpected association retry delay, BSS is terminating");
+				goto fail;
+			} else if (wpa_s->wnm_mode &
+				   WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+				disallowed_sec = WPA_GET_LE16(pos);
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Association retry delay: %u",
+					   disallowed_sec);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Association retry delay attribute not in disassoc imminent mode");
+			}
+
+			break;
+		case MBO_ATTR_ID_AP_CAPA_IND:
+		case MBO_ATTR_ID_NON_PREF_CHAN_REPORT:
+		case MBO_ATTR_ID_CELL_DATA_CAPA:
+		case MBO_ATTR_ID_ASSOC_DISALLOW:
+		case MBO_ATTR_ID_TRANSITION_REJECT_REASON:
+			wpa_printf(MSG_DEBUG,
+				   "MBO: Attribute %d should not be included in BTM Request frame",
+				   id);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u",
+				   id);
+			return;
+		}
+
+		pos += elen;
+		len -= elen;
+	}
+
+	if (cell_pref)
+		wpa_printf(MSG_INFO, "preference=%u",
+			*cell_pref);
+
+	if (wpa_s->wnm_mbo_trans_reason_present)
+		wpa_printf(MSG_INFO, "reason=%u",
+			wpa_s->wnm_mbo_transition_reason);
+
+	if (disallowed_sec && wpa_s->current_bss)
+		wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
+				     disallowed_sec, 0);
+
+	return;
+fail:
+	wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)",
+		   id, elen, len);
+}
+
+
+size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
+				    size_t len,
+				    enum mbo_transition_reject_reason reason)
+{
+	u8 reject_attr[3];
+
+	reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON;
+	reject_attr[1] = 1;
+	reject_attr[2] = reason;
+
+	return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr));
+}
+
+
+#ifndef ESP_SUPPLICANT
+void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa)
+{
+	u8 cell_capa[7];
+
+	if (wpa_s->mbo_cell_capa == mbo_cell_capa) {
+		wpa_printf(MSG_DEBUG,
+			   "MBO: Cellular capability already set to %u",
+			   mbo_cell_capa);
+		return;
+	}
+
+	wpa_s->mbo_cell_capa = mbo_cell_capa;
+
+	cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC;
+	cell_capa[1] = 5; /* Length */
+	WPA_PUT_BE24(cell_capa + 2, OUI_WFA);
+	cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA;
+	cell_capa[6] = mbo_cell_capa;
+
+	wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7);
+	wpa_supplicant_set_default_scan_ies(wpa_s);
+}
+
+
+struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
+				   struct wpa_bss *bss, u32 mbo_subtypes)
+{
+	struct wpabuf *anqp_buf;
+	u8 *len_pos;
+	u8 i;
+
+	if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
+		wpa_printf(MSG_INFO, "MBO: " MACSTR
+			   " does not support MBO - cannot request MBO ANQP elements from it",
+			   MAC2STR(bss->bssid));
+		return NULL;
+	}
+
+	/* Allocate size for the maximum case - all MBO subtypes are set */
+	anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE);
+	if (!anqp_buf)
+		return NULL;
+
+	len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC);
+	wpabuf_put_be24(anqp_buf, OUI_WFA);
+	wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE);
+
+	wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST);
+
+	/* The first valid MBO subtype is 1 */
+	for (i = 1; i <= MAX_MBO_ANQP_SUBTYPE; i++) {
+		if (mbo_subtypes & BIT(i))
+			wpabuf_put_u8(anqp_buf, i);
+	}
+
+	gas_anqp_set_element_len(anqp_buf, len_pos);
+
+	return anqp_buf;
+}
+
+
+void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+			    struct wpa_bss *bss, const u8 *sa,
+			    const u8 *data, size_t slen)
+{
+	const u8 *pos = data;
+	u8 subtype;
+
+	if (slen < 1)
+		return;
+
+	subtype = *pos++;
+	slen--;
+
+	switch (subtype) {
+	case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
+		if (slen < 1)
+			break;
+		wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR
+			" cell_conn_pref=%u", MAC2STR(sa), *pos);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u",
+			   subtype);
+		break;
+	}
+}
+#endif

+ 7 - 0
components/wpa_supplicant/src/common/rrm.c

@@ -851,8 +851,15 @@ wpas_rm_handle_beacon_req(struct wpa_supplicant *wpa_s,
 		goto out;
 	}
 	params->channel = req->channel;
+#ifdef ESP_SUPPLICANT
+	if (params->channel == 0xff) {
+		/* set it to zero */
+		params->channel = 0;
+	}
+#endif
 	params->duration = le_to_host16(req->duration);
 	params->duration_mandatory = duration_mandatory;
+	params->mode = req->mode;
 	if (!params->duration) {
 		wpa_printf(MSG_DEBUG, "Beacon request: Duration is 0");
 		ret = -1;

+ 148 - 1
components/wpa_supplicant/src/common/wnm_sta.c

@@ -191,6 +191,109 @@ static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
 	return NULL;
 }
 
+
+#ifdef CONFIG_MBO
+static struct wpa_bss *
+get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
+			     enum mbo_transition_reject_reason *reason)
+{
+	struct wpa_bss *target = NULL;
+	struct wpa_bss_trans_info params;
+	struct wpa_bss_candidate_info *info = NULL;
+	struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
+	u8 *first_candidate_bssid = NULL, *pos;
+	unsigned int i;
+
+	params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
+	params.n_candidates = 0;
+	params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
+	if (!params.bssid)
+		return NULL;
+
+	pos = params.bssid;
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
+		if (nei->is_first)
+			first_candidate_bssid = nei->bssid;
+		if (!nei->acceptable)
+			continue;
+		os_memcpy(pos, nei->bssid, ETH_ALEN);
+		pos += ETH_ALEN;
+		params.n_candidates++;
+	}
+
+	if (!params.n_candidates)
+		goto end;
+
+#ifndef ESP_SUPPLICANT
+	info = wpa_drv_get_bss_trans_status(wpa_s, &params);
+#endif
+	if (!info) {
+		/* If failed to get candidate BSS transition status from driver,
+		 * get the first acceptable candidate from wpa_supplicant.
+		 */
+		target = wpa_bss_get_bssid(wpa_s, params.bssid);
+		goto end;
+	}
+
+	/* Get the first acceptable candidate from driver */
+	for (i = 0; i < info->num; i++) {
+		if (info->candidates[i].is_accept) {
+			target = wpa_bss_get_bssid(wpa_s,
+						   info->candidates[i].bssid);
+			goto end;
+		}
+	}
+
+	/* If Disassociation Imminent is set and driver rejects all the
+	 * candidate select first acceptable candidate which has
+	 * rssi > disassoc_imminent_rssi_threshold
+	 */
+	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+		for (i = 0; i < info->num; i++) {
+			target = wpa_bss_get_bssid(wpa_s,
+						   info->candidates[i].bssid);
+#ifndef ESP_SUPPLICANT
+			if (target &&
+			    (target->level <
+			     wpa_s->conf->disassoc_imminent_rssi_threshold))
+				continue;
+#else
+			if (target)
+				continue;
+#endif
+			goto end;
+		}
+	}
+
+	/* While sending BTM reject use reason code of the first candidate
+	 * received in BTM request frame
+	 */
+	if (reason) {
+		for (i = 0; i < info->num; i++) {
+			if (first_candidate_bssid &&
+			    os_memcmp(first_candidate_bssid,
+				      info->candidates[i].bssid, ETH_ALEN) == 0)
+			{
+				*reason = info->candidates[i].reject_reason;
+				break;
+			}
+		}
+	}
+
+	target = NULL;
+
+end:
+	os_free(params.bssid);
+	if (info) {
+		os_free(info->candidates);
+		os_free(info);
+	}
+	return target;
+}
+#endif /* CONFIG_MBO */
+
+
+
 /* basic function to match candidate profile with current bss */
 bool wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 			struct wpa_bss *current_bss,
@@ -294,7 +397,14 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
 		nei->acceptable = 1;
 	}
 
+#ifdef CONFIG_MBO
+	if (wpa_s->wnm_mbo_trans_reason_present)
+		target = get_mbo_transition_candidate(wpa_s, reason);
+	else
+		target = get_first_acceptable(wpa_s);
+#else /* CONFIG_MBO */
 	target = get_first_acceptable(wpa_s);
+#endif /* CONFIG_MBO */
 
 	if (target) {
 		wpa_printf(MSG_DEBUG,
@@ -506,6 +616,27 @@ static void wnm_send_bss_transition_mgmt_resp(
 	if (status == WNM_BSS_TM_ACCEPT)
 		wnm_add_cand_list(wpa_s, &buf);
 
+#ifdef CONFIG_MBO
+	if (status != WNM_BSS_TM_ACCEPT &&
+	    wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
+		u8 mbo[10];
+		size_t ret;
+
+		ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
+						   reason);
+		if (ret) {
+			if (wpabuf_resize(&buf, ret) < 0) {
+				wpabuf_free(buf);
+				wpa_printf(MSG_DEBUG,
+					   "WNM: Failed to allocate memory for MBO IE");
+				return;
+			}
+
+			wpabuf_put_data(buf, mbo, ret);
+		}
+	}
+#endif /* CONFIG_MBO */
+
 	res = wpa_drv_send_action(wpa_s, 0, 0,
 				  wpabuf_head_u8(buf), wpabuf_len(buf), 0);
 	if (res < 0) {
@@ -692,8 +823,11 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
 {
 	unsigned int beacon_int;
 	u8 valid_int;
+#ifdef CONFIG_MBO
+	const u8 *vendor;
+#endif /* CONFIG_MBO */
 
-	if (wpa_s->disable_btm)
+	if (wpa_s->disable_mbo_oce || wpa_s->disable_btm)
 		return;
 
 	if (end - pos < 5)
@@ -721,6 +855,19 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
 		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
 		   wpa_s->wnm_dissoc_timer, valid_int);
 
+#if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
+	if (wpa_s->reject_btm_req_reason) {
+		wpa_printf(MSG_INFO,
+			   "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
+			   wpa_s->reject_btm_req_reason);
+		wnm_send_bss_transition_mgmt_resp(
+			wpa_s, wpa_s->wnm_dialog_token,
+			wpa_s->reject_btm_req_reason,
+			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
+		return;
+	}
+#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
+
 	pos += 5;
 
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {

+ 12 - 0
components/wpa_supplicant/src/common/wnm_sta.h

@@ -44,6 +44,9 @@ struct neighbor_report {
 	unsigned int bearing_present:1;
 	unsigned int bss_term_present:1;
 	unsigned int acceptable:1;
+#ifdef CONFIG_MBO
+	unsigned int is_first:1;
+#endif /* CONFIG_MBO */
 	struct measurement_pilot *meas_pilot;
 	struct multiple_bssid *mul_bssid;
 	int freq;
@@ -56,6 +59,13 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
 			      u8 *sender, u8 *payload, size_t len);
 
+void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie,
+			   size_t len);
+
+size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
+				    size_t len,
+				    enum mbo_transition_reject_reason reason);
+
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
 				       u8 query_reason,
 				       const char *btm_candidates,
@@ -66,5 +76,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
 void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
 			struct wpa_bss *bss, char *ssid,
 			int after_new_scan);
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s,
+			      struct wpa_bss *bss);
 
 #endif /* WNM_STA_H */

+ 0 - 1
components/wpa_supplicant/src/common/wpa_common.c

@@ -362,7 +362,6 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
 	return 0;
 }
 
-
 /**
  * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
  * @key: EAPOL-Key Key Confirmation Key (KCK)

+ 2 - 0
components/wpa_supplicant/src/common/wpa_common.h

@@ -324,6 +324,8 @@ const char * wpa_cipher_txt(int cipher);
 
 int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
 			 struct wpa_ie_data *data);
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+		     struct wpa_ie_data *data);
 
 int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
 		      u8 *mic);

+ 60 - 1
components/wpa_supplicant/src/common/wpa_supplicant_i.h

@@ -41,6 +41,16 @@ struct rrm_data {
 	u8 dst_addr[ETH_ALEN];
 };
 
+struct wpa_bss_tmp_disallowed {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+#ifndef ESP_SUPPLICANT
+	int rssi_threshold;
+#else
+	esp_timer_handle_t blacklist_timer;
+#endif
+};
+
 #define SSID_MAX_LEN 32
 struct beacon_rep_data {
 	u8 token;
@@ -61,6 +71,7 @@ enum scan_trigger_reason {
 
 struct wpa_supplicant {
 	int disable_btm;
+	unsigned int disable_mbo_oce;
 	/* rrm ie */
 	uint8_t rrm_ie[5];
 	u8 extend_caps[8];
@@ -101,10 +112,58 @@ struct wpa_supplicant {
 	u8 wnm_bss_termination_duration[12];
 	struct neighbor_report *wnm_neighbor_report_elements;
 	struct os_reltime wnm_cand_valid_until;
+#ifdef CONFIG_MBO
+        unsigned int wnm_mbo_trans_reason_present:1;
+        u8 wnm_mbo_transition_reason;
+	/* Multiband operation non-preferred channel */
+	struct wpa_mbo_non_pref_channel {
+		enum mbo_non_pref_chan_reason reason;
+		u8 oper_class;
+		u8 chan;
+		u8 preference;
+	} *non_pref_chan;
+	size_t non_pref_chan_num;
+	u8 mbo_wnm_token;
+	/**
+	 * enable_oce - Enable OCE if it is enabled by user and device also
+	 *              supports OCE.
+	 * User can enable OCE with wpa_config's 'oce' parameter as follows -
+	 *  - Set BIT(0) to enable OCE in non-AP STA mode.
+	 *  - Set BIT(1) to enable OCE in STA-CFON mode.
+	 */
+	u8 enable_oce;
+	struct dl_list bss_tmp_disallowed;
+#endif /* CONFIG_MBO */
 #endif /* CONFIG_WNM */
 	struct rrm_data rrm;
 	struct beacon_rep_data beacon_rep_data;
 	struct os_reltime beacon_rep_scan;
 };
 
-#endif
+struct non_pref_chan_s;
+
+/* MBO functions */
+int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len,
+		int add_oce_capa);
+const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr);
+const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr);
+const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len,
+				 enum mbo_attr_id attr);
+void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie);
+void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie,
+			   size_t len);
+size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
+				    size_t len,
+				    enum mbo_transition_reject_reason reason);
+void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa);
+struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
+				   struct wpa_bss *bss, u32 mbo_subtypes);
+void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+			    struct wpa_bss *bss, const u8 *sa,
+			    const u8 *data, size_t slen);
+void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s);
+
+int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
+				  struct non_pref_chan_s *non_pref_chan);
+
+#endif /* WPA_SUPPLICANT_I_H */

+ 15 - 0
components/wpa_supplicant/src/drivers/driver.h

@@ -143,6 +143,7 @@ struct wpa_driver_scan_params {
 	 u16 duration;
 
 	 unsigned int duration_mandatory;
+	 u8 mode;
 };
 
 /**
@@ -174,6 +175,20 @@ struct scan_info {
 	u8 scan_start_tsf_bssid[ETH_ALEN];
 } scan_info;
 
+struct wpa_bss_trans_info {
+	u8 mbo_transition_reason;
+	u8 n_candidates;
+	u8 *bssid;
+};
+
+struct wpa_bss_candidate_info {
+	u8 num;
+	struct candidate_list {
+		u8 bssid[ETH_ALEN];
+		u8 is_accept;
+		u32 reject_reason;
+	} *candidates;
+};
 
 /* driver_common.c */
 void wpa_scan_results_free(struct wpa_scan_results *res);

+ 35 - 0
examples/wifi/roaming/main/roaming_example.c

@@ -6,6 +6,7 @@
 #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_system.h"
@@ -40,6 +41,7 @@ static void event_handler(void* arg, esp_event_base_t event_base,
 		esp_wifi_connect();
 	} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
 		wifi_event_sta_disconnected_t *disconn = event_data;
+		ESP_LOGI(TAG, "station got disconnected reason=%d", disconn->reason);
 		if (disconn->reason == WIFI_REASON_ROAMING) {
 			ESP_LOGI(TAG, "station roaming, do nothing");
 		} else {
@@ -323,6 +325,37 @@ static void esp_bss_rssi_low_handler(void* arg, esp_event_base_t event_base,
 }
 #endif
 
+#if 0
+/* Example code to update channel preference in MBO */
+static void clear_chan_preference()
+{
+	esp_mbo_update_non_pref_chan(NULL);
+}
+
+static void update_chan_preference(void)
+{
+	struct non_pref_chan_s *chans = malloc(sizeof(struct non_pref_chan_s) + 2 * sizeof(struct non_pref_chan));
+	chans->non_pref_chan_num = 2;
+
+	/* first */
+	struct non_pref_chan *chan = &chans->chan[0];
+	chan->reason = NON_PREF_CHAN_REASON_UNSPECIFIED;
+	chan->oper_class = 0x51;
+	chan->chan = 1;
+	chan->preference = 0;
+
+	/* second */
+	chan = &chans->chan[1];
+	chan->reason = NON_PREF_CHAN_REASON_UNSPECIFIED;
+	chan->oper_class = 0x51;
+	chan->chan = 11;
+	chan->preference = 1;
+
+	esp_mbo_update_non_pref_chan(chans);
+	free(chans);
+}
+#endif
+
 static void initialise_wifi(void)
 {
 	ESP_ERROR_CHECK(esp_netif_init());
@@ -348,6 +381,8 @@ static void initialise_wifi(void)
 			.password = EXAMPLE_WIFI_PASSWORD,
 			.rm_enabled =1,
 			.btm_enabled =1,
+			.mbo_enabled =1,
+			.pmf_cfg.capable = 1,
 		},
 	};
 

+ 1 - 0
examples/wifi/roaming/sdkconfig.defaults

@@ -1,2 +1,3 @@
 CONFIG_WPA_11KV_SUPPORT=y
 CONFIG_WPA_SCAN_CACHE=y
+CONFIG_WPA_MBO_SUPPORT=y

+ 1 - 7
tools/ci/check_copyright_ignore.txt

@@ -1229,7 +1229,6 @@ components/esp_wifi/include/esp_wifi.h
 components/esp_wifi/include/esp_wifi_crypto_types.h
 components/esp_wifi/include/esp_wifi_default.h
 components/esp_wifi/include/esp_wifi_netif.h
-components/esp_wifi/include/esp_wifi_types.h
 components/esp_wifi/include/smartconfig_ack.h
 components/esp_wifi/src/coexist.c
 components/esp_wifi/src/lib_printf.c
@@ -2979,24 +2978,18 @@ components/wifi_provisioning/src/wifi_provisioning_priv.h
 components/wifi_provisioning/src/wifi_scan.c
 components/wpa_supplicant/esp_supplicant/include/esp_dpp.h
 components/wpa_supplicant/esp_supplicant/include/esp_rrm.h
-components/wpa_supplicant/esp_supplicant/include/esp_wnm.h
 components/wpa_supplicant/esp_supplicant/include/esp_wpa.h
 components/wpa_supplicant/esp_supplicant/include/esp_wpa2.h
 components/wpa_supplicant/esp_supplicant/include/esp_wps.h
-components/wpa_supplicant/esp_supplicant/src/esp_common.c
-components/wpa_supplicant/esp_supplicant/src/esp_common_i.h
 components/wpa_supplicant/esp_supplicant/src/esp_dpp.c
 components/wpa_supplicant/esp_supplicant/src/esp_dpp_i.h
 components/wpa_supplicant/esp_supplicant/src/esp_hostap.c
 components/wpa_supplicant/esp_supplicant/src/esp_hostap.h
-components/wpa_supplicant/esp_supplicant/src/esp_scan.c
 components/wpa_supplicant/esp_supplicant/src/esp_scan_i.h
-components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h
 components/wpa_supplicant/esp_supplicant/src/esp_wpa2.c
 components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c
 components/wpa_supplicant/esp_supplicant/src/esp_wpa3_i.h
 components/wpa_supplicant/esp_supplicant/src/esp_wpa_err.h
-components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c
 components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c
 components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h
 components/wpa_supplicant/esp_supplicant/src/esp_wps.c
@@ -3027,6 +3020,7 @@ components/wpa_supplicant/src/common/eapol_common.h
 components/wpa_supplicant/src/common/ieee802_11_common.c
 components/wpa_supplicant/src/common/ieee802_11_common.h
 components/wpa_supplicant/src/common/ieee802_11_defs.h
+components/wpa_supplicant/src/common/mbo.c
 components/wpa_supplicant/src/common/rrm.c
 components/wpa_supplicant/src/common/rrm.h
 components/wpa_supplicant/src/common/sae.c