Snap for 7316203 from f8b40ebbb234e768d19ca5933ea58dd4a025fd60 to rvc-platform-release

Change-Id: I5834cd3f357714f20d3b976a3370707dd596887d
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 6a2de1f..1a5f899 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -464,6 +464,13 @@
 		event.assoc_info.fils_pmkid = nla_data(fils_pmkid);
 
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+
+	/* Avoid a race condition by stopping to ignore any following
+	 * disconnection events now that the driver has indicated it is
+	 * connected since that connection could have been triggered by a roam
+	 * operation that happened in parallel with the disconnection request.
+	 */
+	drv->ignore_next_local_disconnect = 0;
 }
 
 
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 3994ec0..05fd593 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -595,14 +595,12 @@
 			goto out;
 		}
 
+		dev = p2p_get_device(p2p, sa);
 		if (!dev) {
-			dev = p2p_get_device(p2p, sa);
-			if (!dev) {
-				p2p_dbg(p2p,
-					"Provision Discovery device not found "
-					MACSTR, MAC2STR(sa));
-				goto out;
-			}
+			p2p_dbg(p2p,
+				"Provision Discovery device not found "
+				MACSTR, MAC2STR(sa));
+			goto out;
 		}
 	} else if (msg.wfd_subelems) {
 		wpabuf_free(dev->info.wfd_subelems);
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index e1d9824..173be09 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -4305,6 +4305,7 @@
 	config->disassoc_imminent_rssi_threshold =
 		DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
 	config->oce = DEFAULT_OCE_SUPPORT;
+	config->btm_offload = DEFAULT_BTM_OFFLOAD;
 #endif /* CONFIG_MBO */
 
 	if (ctrl_interface)
@@ -5048,6 +5049,7 @@
 		    MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
 	{ INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
 	{ INT_RANGE(oce, 0, 3), 0 },
+	{ INT_RANGE(btm_offload, 0, 1), CFG_CHANGED_DISABLE_BTM_NOTIFY },
 #endif /* CONFIG_MBO */
 	{ INT(gas_address3), 0 },
 	{ INT_RANGE(ftm_responder, 0, 1), 0 },
@@ -5065,6 +5067,7 @@
 	{ INT_RANGE(disable_btm, 0, 1), CFG_CHANGED_DISABLE_BTM },
 	{ INT_RANGE(extended_key_id, 0, 1), 0 },
 #endif /* CONFIG_WNM */
+	{ INT_RANGE(wowlan_disconnect_on_deinit, 0, 1), 0},
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 0ca27cb..712e871 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -45,6 +45,7 @@
 #define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75
 #define DEFAULT_OCE_SUPPORT OCE_STA
 #define DEFAULT_EXTENDED_KEY_ID 0
+#define DEFAULT_BTM_OFFLOAD 0
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -376,6 +377,7 @@
 #define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
 #define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18)
 #define CFG_CHANGED_DISABLE_BTM BIT(19)
+#define CFG_CHANGED_DISABLE_BTM_NOTIFY BIT(20)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -1447,6 +1449,14 @@
 	 *  - Set BIT(1) to enable OCE in STA-CFON mode
 	 */
 	unsigned int oce;
+
+	/**
+	 * btm_offload - Set where to perform roaming logic
+	 *  - Set to 0 to handle fully roaming logic in supplicant
+	 *  - Set to 1 to skip roaming logic in supplicant for firmware roaming
+	 *    just parse BTM frame and notify framework
+	 */
+        int btm_offload;
 #endif /* CONFIG_MBO */
 
 	/**
@@ -1590,6 +1600,15 @@
 	 * 1 = use Extended Key ID when possible
 	 */
 	int extended_key_id;
+
+	/**
+	 * wowlan_disconnect_on_deinit - Trigger disconnect on wpa_supplicant
+	 * interface deinit even if the driver has enabled WoWLAN.
+	 *
+	 * 0 = Do not disconnect
+	 * 1 = Trigger disconnection
+	 */
+	int wowlan_disconnect_on_deinit;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 52e1372..5faed4e 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1607,6 +1607,9 @@
 	if (config->extended_key_id != DEFAULT_EXTENDED_KEY_ID)
 		fprintf(f, "extended_key_id=%d\n",
 			config->extended_key_id);
+	if (config->wowlan_disconnect_on_deinit)
+		fprintf(f, "wowlan_disconnect_on_deinit=%d\n",
+			config->wowlan_disconnect_on_deinit);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h
index 510668d..a408288 100644
--- a/wpa_supplicant/ctrl_iface.h
+++ b/wpa_supplicant/ctrl_iface.h
@@ -70,14 +70,17 @@
 
 /**
  * wpa_supplicant_ctrl_iface_deinit - Deinitialize control interface
+ * @wpa_s: Pointer to wpa_supplicant data
  * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
  *
  * Deinitialize the control interface that was initialized with
- * wpa_supplicant_ctrl_iface_init().
+ * wpa_supplicant_ctrl_iface_init() and any data related to the wpa_s instance.
+ * @priv may be %NULL if the control interface has not yet been initialized.
  *
  * Required to be implemented in each control interface backend.
  */
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv);
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct ctrl_iface_priv *priv);
 
 /**
  * wpa_supplicant_ctrl_iface_wait - Wait for ctrl_iface monitor
diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c
index 79ff787..bddc041 100644
--- a/wpa_supplicant/ctrl_iface_named_pipe.c
+++ b/wpa_supplicant/ctrl_iface_named_pipe.c
@@ -462,8 +462,11 @@
 }
 
 
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct ctrl_iface_priv *priv)
 {
+	if (!priv)
+		return;
 	while (priv->ctrl_dst)
 		ctrl_close_pipe(priv->ctrl_dst);
 	if (priv->sec_attr_set)
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 1512080..1cbf7fa 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -490,8 +490,12 @@
 }
 
 
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct ctrl_iface_priv *priv)
 {
+	if (!priv)
+		return;
+
 	if (priv->sock > -1) {
 		eloop_unregister_read_sock(priv->sock);
 		if (priv->ctrl_dst) {
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 953fd2c..639573d 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -800,12 +800,52 @@
 }
 
 
-void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+static void
+wpas_global_ctrl_iface_flush_queued_msg(struct wpa_global *global,
+					struct wpa_supplicant *wpa_s)
+{
+	struct ctrl_iface_global_priv *gpriv;
+	struct ctrl_iface_msg *msg, *prev_msg;
+	unsigned int count = 0;
+
+	if (!global || !global->ctrl_iface)
+		return;
+
+	gpriv = global->ctrl_iface;
+	dl_list_for_each_safe(msg, prev_msg, &gpriv->msg_queue,
+			      struct ctrl_iface_msg, list) {
+		if (msg->wpa_s == wpa_s) {
+			count++;
+			dl_list_del(&msg->list);
+			os_free(msg);
+		}
+	}
+
+	if (count) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL: Dropped %u pending message(s) for interface that is being removed",
+			count);
+	}
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct ctrl_iface_priv *priv)
 {
 	struct wpa_ctrl_dst *dst, *prev;
 	struct ctrl_iface_msg *msg, *prev_msg;
 	struct ctrl_iface_global_priv *gpriv;
 
+	if (!priv) {
+		/* Control interface has not yet been initialized, so there is
+		 * nothing to deinitialize here. However, there might be a
+		 * pending message for this interface, so get rid of any such
+		 * entry before completing interface removal. */
+		wpas_global_ctrl_iface_flush_queued_msg(wpa_s->global, wpa_s);
+		eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, wpa_s, NULL);
+		return;
+	}
+
 	if (priv->sock > -1) {
 		char *fname;
 		char *buf, *dir = NULL;
@@ -877,6 +917,7 @@
 			}
 		}
 	}
+	wpas_global_ctrl_iface_flush_queued_msg(wpa_s->global, wpa_s);
 	eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, priv->wpa_s, NULL);
 	os_free(priv);
 }
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 9f69736..11eee98 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -674,10 +674,8 @@
 	os_free(e->radius_conf);
 	e->radius_conf = NULL;
 	scard_deinit(wpa_s->scard);
-	if (wpa_s->ctrl_iface) {
-		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
-		wpa_s->ctrl_iface = NULL;
-	}
+	wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
+	wpa_s->ctrl_iface = NULL;
 
 	ext_password_deinit(wpa_s->ext_pw);
 	wpa_s->ext_pw = NULL;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 7b68ebe..096fd02 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -4586,6 +4586,11 @@
 			break;
 		}
 #endif /* CONFIG_TESTING_OPTIONS */
+		if (wpa_s->disconnected) {
+			wpa_printf(MSG_INFO,
+				   "Ignore unexpected EVENT_ASSOC in disconnected state");
+			break;
+		}
 		wpa_supplicant_event_assoc(wpa_s, data);
 		wpa_s->assoc_status_code = WLAN_STATUS_SUCCESS;
 		if (data &&
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 4a8f4ff..dc955b8 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -192,10 +192,8 @@
 	pmksa_candidate_free(wpa_s->wpa);
 	wpa_sm_deinit(wpa_s->wpa);
 	scard_deinit(wpa_s->scard);
-	if (wpa_s->ctrl_iface) {
-		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
-		wpa_s->ctrl_iface = NULL;
-	}
+	wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
+	wpa_s->ctrl_iface = NULL;
 	wpa_config_free(wpa_s->conf);
 }
 
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 2e09102..ac04383 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -1451,6 +1451,22 @@
 			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
 	}
 
+#ifdef CONFIG_MBO
+	vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
+	if (vendor) {
+		wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
+		if (wpa_s->conf->btm_offload) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"WNM: Notify BSS Transition Management Request frame status");
+			wpa_s->bss_tm_status = WNM_BSS_TM_ACCEPT;
+			wpas_notify_bss_tm_status(wpa_s);
+			/* since it could be referenced in the scan result logic, initialize it */
+			wpa_s->wnm_mbo_trans_reason_present = 0;
+			return;
+		}
+	}
+#endif /* CONFIG_MBO */
+
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
 		wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
 			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
@@ -1462,12 +1478,6 @@
 		}
 	}
 
-#ifdef CONFIG_MBO
-	vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
-	if (vendor)
-		wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
-#endif /* CONFIG_MBO */
-
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
 		unsigned int valid_ms;
 
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index a01a4e5..f928fdb 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1137,8 +1137,8 @@
 		    os_strcmp(conf->ctrl_interface,
 			      wpa_s->conf->ctrl_interface) != 0);
 
-	if (reconf_ctrl && wpa_s->ctrl_iface) {
-		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+	if (reconf_ctrl) {
+		wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
 		wpa_s->ctrl_iface = NULL;
 	}
 
@@ -6493,8 +6493,12 @@
 
 	wpa_s->disconnected = 1;
 	if (wpa_s->drv_priv) {
-		/* Don't deauthenticate if WoWLAN is enabled */
-		if (!wpa_drv_get_wowlan(wpa_s)) {
+		/*
+		 * Don't deauthenticate if WoWLAN is enabled and not explicitly
+		 * been configured to disconnect.
+		 */
+		if (!wpa_drv_get_wowlan(wpa_s) ||
+		    wpa_s->conf->wowlan_disconnect_on_deinit) {
 			wpa_supplicant_deauthenticate(
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 
@@ -6532,10 +6536,8 @@
 	if (terminate)
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING);
 
-	if (wpa_s->ctrl_iface) {
-		wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
-		wpa_s->ctrl_iface = NULL;
-	}
+	wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
+	wpa_s->ctrl_iface = NULL;
 
 #ifdef CONFIG_MESH
 	if (wpa_s->ifmsh) {
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 3b90567..f5194be 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1584,6 +1584,12 @@
 # Set to 1 to disable BSS transition management
 #disable_btm=0
 
+# This value is used to set where to perform roaming logic
+# Set to 0 to handle roaming logic fully in supplicant
+# Set to 1 to skip roaming logic in supplicant and handle it in firmware
+# In supplicant, just parse BTM frame and notify framework
+#btm_offload=0
+
 # Enable EDMG capability in STA/AP mode, default value is false
 #enable_edmg=1
 
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index fce7e5e..552477d 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -6,3 +6,4 @@
 pmf=1
 p2p_add_cli_chan=1
 oce=1
+wowlan_disconnect_on_deinit=1