Revert "Revert "[wpa_supplicant] cumilative patch from commit 4b..."

Revert submission 28102966-revert-26533062-Supplicant_merge_June24-CUATTSRBBR

Fixed the regression issue (ag/28389573)
Bug: 329004037

Reverted changes: /q/submissionid:28102966-revert-26533062-Supplicant_merge_June24-CUATTSRBBR

Test: Turn ON/OFF SoftAp

Change-Id: Ie7ea1ee7f8b1311fce280907d37a2e321542f547
diff --git a/wpa_supplicant/Android.bp b/wpa_supplicant/Android.bp
index c0835ae..afcc04b 100644
--- a/wpa_supplicant/Android.bp
+++ b/wpa_supplicant/Android.bp
@@ -315,6 +315,7 @@
         "src/common/sae.c",
         "src/common/sae_pk.c",
         "src/common/wpa_common.c",
+        "src/common/ptksa_cache.c",
         "src/crypto/aes-ctr.c",
         "src/crypto/aes-encblock.c",
         "src/crypto/aes-siv.c",
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index baf3ad1..b667d82 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -139,6 +139,8 @@
 OBJS += src/utils/bitfield.c
 OBJS += src/utils/ip_addr.c
 OBJS += src/utils/crc32.c
+OBJS += src/common/ptksa_cache.c
+OBJS += src/rsn_supp/pmksa_cache.c
 OBJS += twt.c
 OBJS_p = wpa_passphrase.c
 OBJS_p += src/utils/common.c
@@ -379,7 +381,6 @@
 ifndef CONFIG_NO_WPA
 OBJS += src/rsn_supp/wpa.c
 OBJS += src/rsn_supp/preauth.c
-OBJS += src/rsn_supp/pmksa_cache.c
 OBJS += src/rsn_supp/wpa_ie.c
 OBJS += src/common/wpa_common.c
 NEED_AES=y
@@ -432,8 +433,8 @@
 NEED_HMAC_SHA384_KDF=y
 NEED_SHA256=y
 NEED_SHA384=y
-OBJS += src/common/ptksa_cache.c
 OBJS += src/pasn/pasn_initiator.c
+OBJS += src/pasn/pasn_common.c
 OBJS += pasn_supplicant.c
 endif
 
@@ -1846,8 +1847,9 @@
 
 PASNOBJS += src/common/ptksa_cache.c
 
-ifndef CONFIG_NO_WPA
 PASNOBJS += src/rsn_supp/pmksa_cache.c
+
+ifndef CONFIG_NO_WPA
 PASNOBJS += src/rsn_supp/wpa_ie.c
 endif
 
@@ -1933,6 +1935,7 @@
 
 PASNOBJS += src/pasn/pasn_initiator.c
 PASNOBJS += src/pasn/pasn_responder.c
+PASNOBJS += src/pasn/pasn_common.c
 
 ########################
 
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 93fc338..8bec178 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -112,6 +112,8 @@
 OBJS += ../src/utils/bitfield.o
 OBJS += ../src/utils/ip_addr.o
 OBJS += ../src/utils/crc32.o
+OBJS += ../src/common/ptksa_cache.o
+OBJS += ../src/rsn_supp/pmksa_cache.o
 OBJS += twt.o
 OBJS_p = wpa_passphrase.o
 OBJS_p += ../src/utils/common.o
@@ -374,7 +376,6 @@
 ifndef CONFIG_NO_WPA
 OBJS += ../src/rsn_supp/wpa.o
 OBJS += ../src/rsn_supp/preauth.o
-OBJS += ../src/rsn_supp/pmksa_cache.o
 OBJS += ../src/rsn_supp/wpa_ie.o
 OBJS += ../src/common/wpa_common.o
 NEED_AES=y
@@ -435,8 +436,8 @@
 NEED_HMAC_SHA384_KDF=y
 NEED_SHA256=y
 NEED_SHA384=y
-OBJS += ../src/common/ptksa_cache.o
 OBJS += ../src/pasn/pasn_initiator.o
+OBJS += ../src/pasn/pasn_common.o
 OBJS += pasn_supplicant.o
 endif
 
@@ -1187,6 +1188,10 @@
 CFLAGS += -DCONFIG_TLSV12
 endif
 
+ifdef CONFIG_RADIUS_TLS
+TLS_FUNCS=y
+endif
+
 ifeq ($(CONFIG_TLS), wolfssl)
 ifdef TLS_FUNCS
 CFLAGS += -DWOLFSSL_DER_LOAD
@@ -1925,6 +1930,9 @@
 OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
 OBJS_t += ../src/radius/radius_client.o
 OBJS_t += ../src/radius/radius.o
+ifdef CONFIG_RADIUS_TLS
+CFLAGS += -DCONFIG_RADIUS_TLS
+endif
 OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
 
 OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o
@@ -2219,9 +2227,9 @@
 endif
 
 LIBPASNSO += ../src/common/ptksa_cache.c
+LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
 
 ifndef CONFIG_NO_WPA
-LIBPASNSO += ../src/rsn_supp/pmksa_cache.c
 LIBPASNSO += ../src/rsn_supp/wpa_ie.c
 endif
 
@@ -2316,6 +2324,7 @@
 
 LIBPASNSO += ../src/pasn/pasn_initiator.c
 LIBPASNSO += ../src/pasn/pasn_responder.c
+LIBPASNSO += ../src/pasn/pasn_common.c
 
 libpasn.so: $(LIBPASNSO)
 	@$(E) "  CC  $@ ($^)"
diff --git a/wpa_supplicant/aidl/supplicant.cpp b/wpa_supplicant/aidl/supplicant.cpp
index 1589dd2..ae1943f 100644
--- a/wpa_supplicant/aidl/supplicant.cpp
+++ b/wpa_supplicant/aidl/supplicant.cpp
@@ -413,7 +413,7 @@
 		// Request the current scan results from the driver and update
 		// the local BSS list wpa_s->bss. This is to avoid a full scan
 		// while processing the connect request on newly created interface.
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, NULL);
 	}
 	// The supplicant core creates a corresponding aidl object via
 	// AidlManager when |wpa_supplicant_add_iface| is called.
@@ -472,7 +472,7 @@
 		// Request the current scan results from the driver and update
 		// the local BSS list wpa_s->bss. This is to avoid a full scan
 		// while processing the connect request on newly created interface.
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, NULL);
 	}
 	// The supplicant core creates a corresponding aidl object via
 	// AidlManager when |wpa_supplicant_add_iface| is called.
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 000914a..69a0e5e 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -440,9 +440,14 @@
 		}
 	}
 
-	if (wpa_s->p2p_go_no_pri_sec_switch) {
+#ifdef CONFIG_P2P
+	if (ssid->p2p_group && wpa_s->p2p_go_no_pri_sec_switch) {
 		conf->no_pri_sec_switch = 1;
-	} else if (conf->secondary_channel) {
+		return 0;
+	}
+#endif /* CONFIG_P2P */
+
+	if (conf->secondary_channel) {
 		struct wpa_supplicant *iface;
 
 		for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
@@ -868,7 +873,8 @@
 
 
 static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr,
-				 int authorized, const u8 *p2p_dev_addr, const u8 *ip)
+				 int authorized, const u8 *p2p_dev_addr,
+				 const u8 *ip)
 {
 	wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_addr, ip);
 }
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 22b694c..2890353 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -400,6 +400,11 @@
 	if (bss == wpa_s->ml_connect_probe_bss)
 		return 1;
 
+#ifdef CONFIG_WNM
+	if (bss == wpa_s->wnm_target_bss)
+		return 1;
+#endif /* CONFIG_WNM */
+
 	if (wpa_s->current_bss &&
 	    (bss->ssid_len != wpa_s->current_bss->ssid_len ||
 	     os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
@@ -414,10 +419,7 @@
 	if (!wpa_s->valid_links)
 		return 0;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		if (ether_addr_equal(bss->bssid, wpa_s->links[i].bssid))
 			return 1;
 	}
@@ -1513,9 +1515,6 @@
 	for (i = 0; i < count; i++) {
 		u8 bss_params;
 
-		if (bss->n_mld_links >= MAX_NUM_MLD_LINKS)
-			return;
-
 		if (end - pos < ap_info->tbtt_info_len)
 			break;
 
@@ -1523,6 +1522,8 @@
 		mld_params = pos + mld_params_offset;
 
 		link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK;
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			return;
 
 		if (*mld_params != mbssid_idx) {
 			wpa_printf(MSG_DEBUG,
@@ -1546,13 +1547,12 @@
 					   wpa_s, neigh_bss->bssid)) {
 				struct mld_link *l;
 
-				l = &bss->mld_links[bss->n_mld_links];
-				l->link_id = link_id;
+				bss->valid_links |= BIT(link_id);
+				l = &bss->mld_links[link_id];
 				os_memcpy(l->bssid, pos + 1, ETH_ALEN);
 				l->freq = neigh_bss->freq;
 				l->disabled = mld_params[2] &
 					RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
-				bss->n_mld_links++;
 			}
 		}
 
@@ -1678,15 +1678,15 @@
 		os_memcpy(ap_mld_addr, ml_basic_common_info->mld_addr,
 			  ETH_ALEN);
 
-	bss->n_mld_links = 0;
-	l = &bss->mld_links[bss->n_mld_links];
 	link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
-	l->link_id = link_id;
+
+	bss->mld_link_id = link_id;
+	seen = bss->valid_links = BIT(link_id);
+
+	l = &bss->mld_links[link_id];
 	os_memcpy(l->bssid, bss->bssid, ETH_ALEN);
 	l->freq = bss->freq;
 
-	seen = BIT(link_id);
-	bss->n_mld_links++;
 
 	/*
 	 * The AP MLD ID in the RNR corresponds to the MBSSID index, see
@@ -1736,13 +1736,12 @@
 		}
 	}
 
-	wpa_printf(MSG_DEBUG, "MLD: n_mld_links=%u (unresolved: 0x%04hx)",
-		   bss->n_mld_links, missing);
+	wpa_printf(MSG_DEBUG, "MLD: valid_links=%04hx (unresolved: 0x%04hx)",
+		   bss->valid_links, missing);
 
-	for (i = 0; i < bss->n_mld_links; i++) {
+	for_each_link(bss->valid_links, i) {
 		wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
-			   bss->mld_links[i].link_id,
-			   MAC2STR(bss->mld_links[i].bssid));
+			   i, MAC2STR(bss->mld_links[i].bssid));
 	}
 
 	if (missing_links)
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index c06c20a..cc04963 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -126,11 +126,12 @@
 	size_t beacon_ie_len;
 	/** MLD address of the AP */
 	u8 mld_addr[ETH_ALEN];
+	/** Link ID of this affiliated AP of the AP MLD */
+	u8 mld_link_id;
 
 	/** An array of MLD links */
-	u8 n_mld_links;
+	u16 valid_links;
 	struct mld_link {
-		u8 link_id;
 		u8 bssid[ETH_ALEN];
 		int freq;
 
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 8c0db68..c949bab 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -197,9 +197,10 @@
 #endif /* NO_CONFIG_WRITE */
 
 
-static int wpa_config_parse_int(const struct parse_data *data,
-				struct wpa_ssid *ssid,
-				int line, const char *value)
+static int wpa_config_parse_int_impl(const struct parse_data *data,
+				     struct wpa_ssid *ssid,
+				     int line, const char *value,
+				     bool check_range)
 {
 	int val, *dst;
 	char *end;
@@ -212,31 +213,46 @@
 		return -1;
 	}
 
+	if (check_range && val < (long) data->param3) {
+		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+			   "min_value=%ld)", line, data->name, val,
+			   (long) data->param3);
+		return -1;
+	}
+
+	if (check_range && val > (long) data->param4) {
+		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+			   "max_value=%ld)", line, data->name, val,
+			   (long) data->param4);
+		return -1;
+	}
+
 	if (*dst == val)
 		return 1;
+
 	*dst = val;
 	wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
 
-	if (data->param3 && *dst < (long) data->param3) {
-		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
-			   "min_value=%ld)", line, data->name, *dst,
-			   (long) data->param3);
-		*dst = (long) data->param3;
-		return -1;
-	}
-
-	if (data->param4 && *dst > (long) data->param4) {
-		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
-			   "max_value=%ld)", line, data->name, *dst,
-			   (long) data->param4);
-		*dst = (long) data->param4;
-		return -1;
-	}
-
 	return 0;
 }
 
 
+static int wpa_config_parse_int(const struct parse_data *data,
+				struct wpa_ssid *ssid,
+				int line, const char *value)
+{
+	return wpa_config_parse_int_impl(data, ssid, line, value, false);
+}
+
+
+static int wpa_config_parse_int_range(const struct parse_data *data,
+				      struct wpa_ssid *ssid,
+				      int line, const char *value)
+{
+	return wpa_config_parse_int_impl(data, ssid, line, value, true);
+}
+
+
 #ifndef NO_CONFIG_WRITE
 static char * wpa_config_write_int(const struct parse_data *data,
 				   struct wpa_ssid *ssid)
@@ -2457,7 +2473,14 @@
 #define INTe(f, m) _INTe(f, m), NULL, NULL, 0
 
 /* INT_RANGE: Define an integer variable with allowed value range */
-#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
+#ifdef NO_CONFIG_WRITE
+#define INT_RANGE(f, min, max) #f, wpa_config_parse_int_range, OFFSET(f), \
+	(void *) 0, (void *) (min), (void *) (max), 0
+#else /* NO_CONFIG_WRITE */
+#define INT_RANGE(f, min, max) #f, wpa_config_parse_int_range, \
+	wpa_config_write_int, OFFSET(f),	       \
+	(void *) 0, (void *) (min), (void *) (max), 0
+#endif /* NO_CONFIG_WRITE */
 
 /* FUNC: Define a configuration variable that uses a custom function for
  * parsing and writing the value. */
@@ -2652,7 +2675,7 @@
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_HT_OVERRIDES
 	{ INT_RANGE(disable_ht, 0, 1) },
-	{ INT_RANGE(disable_ht40, -1, 1) },
+	{ INT_RANGE(disable_ht40, 0, 1) },
 	{ INT_RANGE(disable_sgi, 0, 1) },
 	{ INT_RANGE(disable_ldpc, 0, 1) },
 	{ INT_RANGE(ht40_intolerant, 0, 1) },
@@ -2725,6 +2748,8 @@
 	{ INT_RANGE(owe_ptk_workaround, 0, 1) },
 	{ INT_RANGE(multi_ap_backhaul_sta, 0, 1) },
 	{ INT_RANGE(ft_eap_pmksa_caching, 0, 1) },
+	{ INT_RANGE(multi_ap_profile, MULTI_AP_PROFILE_1,
+		    MULTI_AP_PROFILE_MAX) },
 	{ INT_RANGE(beacon_prot, 0, 1) },
 	{ INT_RANGE(transition_disable, 0, 255) },
 	{ INT_RANGE(sae_pk, 0, 2) },
@@ -4743,9 +4768,10 @@
 };
 
 
-static int wpa_global_config_parse_int(const struct global_parse_data *data,
-				       struct wpa_config *config, int line,
-				       const char *pos)
+static int
+wpa_global_config_parse_int_impl(const struct global_parse_data *data,
+				 struct wpa_config *config, int line,
+				 const char *pos, bool check_range)
 {
 	int val, *dst;
 	char *end;
@@ -4758,31 +4784,47 @@
 			   line, pos);
 		return -1;
 	}
+
+	if (check_range && val < (long) data->param2) {
+		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+			   "min_value=%ld)", line, data->name, val,
+			   (long) data->param2);
+		return -1;
+	}
+
+	if (check_range && val > (long) data->param3) {
+		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+			   "max_value=%ld)", line, data->name, val,
+			   (long) data->param3);
+		return -1;
+	}
+
 	same = *dst == val;
 	*dst = val;
 
 	wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
 
-	if (data->param2 && *dst < (long) data->param2) {
-		wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
-			   "min_value=%ld)", line, data->name, *dst,
-			   (long) data->param2);
-		*dst = (long) data->param2;
-		return -1;
-	}
-
-	if (data->param3 && *dst > (long) data->param3) {
-		wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
-			   "max_value=%ld)", line, data->name, *dst,
-			   (long) data->param3);
-		*dst = (long) data->param3;
-		return -1;
-	}
-
 	return same;
 }
 
 
+static int wpa_global_config_parse_int(const struct global_parse_data *data,
+				       struct wpa_config *config, int line,
+				       const char *pos)
+{
+	return wpa_global_config_parse_int_impl(data, config, line, pos, false);
+}
+
+
+static int
+wpa_global_config_parse_int_range(const struct global_parse_data *data,
+				  struct wpa_config *config, int line,
+				  const char *pos)
+{
+	return wpa_global_config_parse_int_impl(data, config, line, pos, true);
+}
+
+
 static int wpa_global_config_parse_str(const struct global_parse_data *data,
 				       struct wpa_config *config, int line,
 				       const char *pos)
@@ -5346,7 +5388,8 @@
 #define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL
 #define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_int, OFFSET(f)
 #define INT(f) _INT(f), NULL, NULL
-#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
+#define INT_RANGE(f, min, max) #f, wpa_global_config_parse_int_range, \
+	wpa_config_get_int, OFFSET(f), (void *) min, (void *) max
 #define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f)
 #define STR(f) _STR(f), NULL, NULL
 #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 71a64b1..414ed22 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -850,6 +850,7 @@
 	INT(owe_ptk_workaround);
 	INT(multi_ap_backhaul_sta);
 	INT(ft_eap_pmksa_caching);
+	INT(multi_ap_profile);
 	INT(beacon_prot);
 	INT(transition_disable);
 	INT(sae_pk);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 785acc3..2043a80 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -1187,6 +1187,11 @@
 	int ft_eap_pmksa_caching;
 
 	/**
+	 * multi_ap_profile - Supported Multi-AP profile
+	 */
+	int multi_ap_profile;
+
+	/**
 	 * beacon_prot - Whether Beacon protection is enabled
 	 *
 	 * This depends on management frame protection (ieee80211w) being
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 500f4d1..d0fda4c 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -902,9 +902,20 @@
 			wpa_config_process_global(wpa_s->conf, cmd, -1);
 		}
 	} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
-		wpas_mbo_update_cell_capa(wpa_s, atoi(value));
+		int val = atoi(value);
+
+		if (val < MBO_CELL_CAPA_AVAILABLE ||
+		    val > MBO_CELL_CAPA_NOT_SUPPORTED)
+			return -1;
+
+		wpas_mbo_update_cell_capa(wpa_s, val);
 	} else if (os_strcasecmp(cmd, "oce") == 0) {
-		wpa_s->conf->oce = atoi(value);
+		int val = atoi(value);
+
+		if (val < 0 || val > 3)
+			return -1;
+
+		wpa_s->conf->oce = val;
 		if (wpa_s->conf->oce) {
 			if ((wpa_s->conf->oce & OCE_STA) &&
 			    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA))
@@ -8850,6 +8861,8 @@
 
 	wpa_s->next_ssid = NULL;
 
+	wnm_btm_reset(wpa_s);
+
 #ifdef CONFIG_INTERWORKING
 #ifdef CONFIG_HS20
 	hs20_cancel_fetch_osu(wpa_s);
@@ -11995,10 +12008,7 @@
 	pos = buf;
 	end = buf + buflen;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(mlo_si.valid_links & BIT(i)))
-			continue;
-
+	for_each_link(mlo_si.valid_links, i) {
 		ret = os_snprintf(pos, end - pos,
 				  "LINK_ID=%d\nRSSI=%d\nLINKSPEED=%lu\n"
 				  "NOISE=%d\nFREQUENCY=%u\n",
@@ -12070,10 +12080,7 @@
 	pos = buf;
 	end = buf + buflen;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		ret = os_snprintf(pos, end - pos, "link_id=%d\nfreq=%u\n"
 				  "ap_link_addr=" MACSTR
 				  "\nsta_link_addr=" MACSTR "\n",
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index fdf7b12..27003eb 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -706,6 +706,59 @@
 }
 
 
+#define UINT16_ARRAY_CHUNK_SIZE 18
+#define UINT16_ARRAY_ITEM_SIZE (sizeof(dbus_uint16_t))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_uint16_array(
+	DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
+{
+	dbus_uint32_t count = 0;
+	dbus_uint16_t *buffer, *nbuffer;
+
+	entry->uint16array_value = NULL;
+	entry->array_type = DBUS_TYPE_UINT16;
+
+	buffer = os_calloc(UINT16_ARRAY_CHUNK_SIZE, UINT16_ARRAY_ITEM_SIZE);
+	if (!buffer)
+		return FALSE;
+
+	entry->array_len = 0;
+	while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_UINT16) {
+		dbus_uint16_t value;
+
+		if ((count % UINT16_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
+			nbuffer = os_realloc_array(
+				buffer, count + UINT16_ARRAY_CHUNK_SIZE,
+				UINT16_ARRAY_ITEM_SIZE);
+			if (!nbuffer) {
+				os_free(buffer);
+				wpa_printf(MSG_ERROR,
+					   "dbus: %s out of memory trying to retrieve the uint16 array",
+					   __func__);
+				return FALSE;
+			}
+			buffer = nbuffer;
+		}
+
+		dbus_message_iter_get_basic(iter, &value);
+		buffer[count] = value;
+		entry->array_len = ++count;
+		dbus_message_iter_next(iter);
+	}
+	entry->uint16array_value = buffer;
+	wpa_hexdump_key(MSG_MSGDUMP, "dbus: uint16 array contents",
+			entry->bytearray_value, entry->array_len);
+
+	/* Zero-length arrays are valid. */
+	if (entry->array_len == 0) {
+		os_free(entry->uint16array_value);
+		entry->uint16array_value = NULL;
+	}
+
+	return TRUE;
+}
+
+
 #define STR_ARRAY_CHUNK_SIZE 8
 #define STR_ARRAY_ITEM_SIZE (sizeof(char *))
 
@@ -873,6 +926,10 @@
 		success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
 							      entry);
 		break;
+	case DBUS_TYPE_UINT16:
+		success = _wpa_dbus_dict_entry_get_uint16_array(&iter_array,
+								entry);
+		break;
 	case DBUS_TYPE_STRING:
 		success = _wpa_dbus_dict_entry_get_string_array(&iter_array,
 								array_type,
@@ -1081,6 +1138,9 @@
 		case DBUS_TYPE_BYTE:
 			os_free(entry->bytearray_value);
 			break;
+		case DBUS_TYPE_UINT16:
+			os_free(entry->uint16array_value);
+			break;
 		case DBUS_TYPE_STRING:
 			if (!entry->strarray_value)
 				break;
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index cc9e26f..1d33689 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -139,6 +139,7 @@
 		dbus_uint64_t uint64_value;
 		double double_value;
 		char *bytearray_value;
+		dbus_uint16_t *uint16array_value;
 		char **strarray_value;
 		struct wpabuf **binarray_value;
 	};
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 00b38ed..8bd6a9a 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -1023,6 +1023,40 @@
 	dbus_message_unref(msg);
 }
 
+
+void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s,
+				      const u8 *dst, const char *result)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+	DBusMessageIter iter;
+	char addr[WPAS_DBUS_OBJECT_PATH_MAX], *bssid;
+
+	os_snprintf(addr, WPAS_DBUS_OBJECT_PATH_MAX, MACSTR, MAC2STR(dst));
+	bssid = addr;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (!iface || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_INTERFACE,
+				      "ANQPQueryDone");
+	if (!msg)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bssid) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &result))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
 #endif /* CONFIG_INTERWORKING */
 
 
@@ -2439,6 +2473,9 @@
 	case WPAS_DBUS_BSS_PROP_AGE:
 		prop = "Age";
 		break;
+	case WPAS_DBUS_BSS_PROP_ANQP:
+		prop = "ANQP";
+		break;
 	default:
 		wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
 			   __func__, property);
@@ -2977,6 +3014,11 @@
 	  NULL,
 	  NULL
 	},
+	{"ANQP", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+	  wpas_dbus_getter_bss_anqp,
+	  NULL,
+	  NULL,
+	},
 	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -3716,6 +3758,13 @@
 		  END_ARGS
 	  }
 	},
+	{"ANQPGet", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_anqp_get,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  },
+	},
 #endif /* CONFIG_INTERWORKING */
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
@@ -4303,6 +4352,13 @@
 		  END_ARGS
 	  }
 	},
+	{"ANQPQueryDone", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "addr", "s", ARG_OUT },
+		  { "result", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
 	{ "HS20TermsAndConditions", WPAS_DBUS_NEW_IFACE_INTERFACE,
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index b653f10..952bb42 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -53,6 +53,7 @@
 	WPAS_DBUS_BSS_PROP_WPS,
 	WPAS_DBUS_BSS_PROP_IES,
 	WPAS_DBUS_BSS_PROP_AGE,
+	WPAS_DBUS_BSS_PROP_ANQP,
 };
 
 enum wpas_dbus_sta_prop {
@@ -279,6 +280,8 @@
 					    int bh, int bss_load,
 					    int conn_capab);
 void wpas_dbus_signal_interworking_select_done(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s,
+				      const u8 *dst, const char *result);
 void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
 					  const char *url);
 
@@ -653,6 +656,12 @@
 }
 
 static inline
+void wpas_dbus_signal_anqp_query_done(struct wpa_supplicant *wpa_s,
+				      const u8 *dst, const char *result)
+{
+}
+
+static inline
 void wpas_dbus_signal_hs20_t_c_acceptance(struct wpa_supplicant *wpa_s,
 					  const char *url)
 {
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 6ad49a1..3897d98 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -1957,6 +1957,7 @@
 
 
 #ifdef CONFIG_INTERWORKING
+
 DBusMessage *
 wpas_dbus_handler_interworking_select(DBusMessage *message,
 				      struct wpa_supplicant *wpa_s)
@@ -1977,6 +1978,111 @@
 
 	return reply;
 }
+
+
+DBusMessage *
+wpas_dbus_handler_anqp_get(DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter	iter, iter_dict;
+	struct wpa_dbus_dict_entry entry;
+	int ret;
+	u8 dst_addr[ETH_ALEN];
+	bool is_addr_present = false;
+	unsigned int freq = 0;
+#define MAX_ANQP_INFO_ID 100 /* Max info ID count from CLI implementation */
+	u16 id[MAX_ANQP_INFO_ID];
+	size_t num_id = 0;
+	u32 subtypes = 0;
+	u32 mbo_subtypes = 0;
+	size_t i;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		return wpas_dbus_error_invalid_args(message, NULL);
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			return wpas_dbus_error_invalid_args(message, NULL);
+
+		if (os_strcmp(entry.key, "addr") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			if (hwaddr_aton(entry.str_value, dst_addr)) {
+				wpa_printf(MSG_DEBUG,
+					   "%s[dbus]: Invalid address '%s'",
+					   __func__, entry.str_value);
+				wpa_dbus_dict_entry_clear(&entry);
+				return wpas_dbus_error_invalid_args(
+					message, "invalid address");
+			}
+
+			is_addr_present = true;
+		} else if (os_strcmp(entry.key, "freq") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
+			freq = entry.uint32_value;
+		} else if (os_strcmp(entry.key, "ids") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_UINT16) {
+			for (i = 0; i < entry.array_len &&
+				     num_id < MAX_ANQP_INFO_ID; i++) {
+				id[num_id] = entry.uint16array_value[i];
+				num_id++;
+			}
+		} else if (os_strcmp(entry.key, "hs20_ids") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			for (i = 0; i < entry.array_len; i++) {
+				int num = entry.bytearray_value[i];
+
+				if (num <= 0 || num > 31) {
+					wpa_dbus_dict_entry_clear(&entry);
+					return wpas_dbus_error_invalid_args(
+						message,
+						"invalid HS20 ANQP id");
+				}
+				subtypes |= BIT(num);
+			}
+		} else if (os_strcmp(entry.key, "mbo_ids") == 0 &&
+			   entry.type == DBUS_TYPE_ARRAY &&
+			   entry.array_type == DBUS_TYPE_BYTE) {
+			for (i = 0; i < entry.array_len; i++) {
+				int num = entry.bytearray_value[i];
+
+				if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE) {
+					wpa_dbus_dict_entry_clear(&entry);
+					return wpas_dbus_error_invalid_args(
+						message, "invalid MBO ANQP id");
+				}
+				mbo_subtypes |= BIT(num);
+			}
+		} else {
+			wpa_dbus_dict_entry_clear(&entry);
+			return wpas_dbus_error_invalid_args(
+				message, "unsupported parameter");
+		}
+
+		wpa_dbus_dict_entry_clear(&entry);
+	}
+
+	if (!is_addr_present) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: address not provided", __func__);
+		return wpas_dbus_error_invalid_args(message,
+						    "address not provided");
+	}
+
+	ret = anqp_send_req(wpa_s, dst_addr, freq, id, num_id, subtypes,
+			    mbo_subtypes);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s[dbus]: failed to send ANQP request",
+			   __func__);
+		return wpas_dbus_error_unknown_error(
+			message, "error sending ANQP request");
+	}
+
+	return NULL;
+}
+
 #endif /* CONFIG_INTERWORKING */
 
 
@@ -5652,6 +5758,177 @@
 
 
 /**
+ * wpas_dbus_getter_bss_anqp - Return all the ANQP fields of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ANQP" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_anqp(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	DBusMessageIter iter_dict, variant_iter;
+	struct bss_handler_args *args = user_data;
+	struct wpa_bss *bss;
+	struct wpa_bss_anqp *anqp;
+	struct wpa_bss_anqp_elem *elem;
+
+	bss = get_bss_helper(args, error, __func__);
+	if (!bss)
+		return FALSE;
+
+	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+					      "a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+		goto nomem;
+
+	anqp = bss->anqp;
+	if (anqp) {
+#ifdef CONFIG_INTERWORKING
+		if (anqp->capability_list &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "CapabilityList",
+			    wpabuf_head(anqp->capability_list),
+			    wpabuf_len(anqp->capability_list)))
+			goto nomem;
+		if (anqp->venue_name &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "VenueName",
+			    wpabuf_head(anqp->venue_name),
+			    wpabuf_len(anqp->venue_name)))
+			goto nomem;
+		if (anqp->network_auth_type &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "NetworkAuthType",
+			    wpabuf_head(anqp->network_auth_type),
+			    wpabuf_len(anqp->network_auth_type)))
+			goto nomem;
+		if (anqp->roaming_consortium &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "RoamingConsortium",
+			    wpabuf_head(anqp->roaming_consortium),
+			    wpabuf_len(anqp->roaming_consortium)))
+			goto nomem;
+		if (anqp->ip_addr_type_availability &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "IPAddrTypeAvailability",
+			    wpabuf_head(anqp->ip_addr_type_availability),
+			    wpabuf_len(anqp->ip_addr_type_availability)))
+			goto nomem;
+		if (anqp->nai_realm &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "NAIRealm",
+			    wpabuf_head(anqp->nai_realm),
+			    wpabuf_len(anqp->nai_realm)))
+			goto nomem;
+		if (anqp->anqp_3gpp &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "3GPP",
+			    wpabuf_head(anqp->anqp_3gpp),
+			    wpabuf_len(anqp->anqp_3gpp)))
+			goto nomem;
+		if (anqp->domain_name &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "DomainName",
+			    wpabuf_head(anqp->domain_name),
+			    wpabuf_len(anqp->domain_name)))
+			goto nomem;
+		if (anqp->fils_realm_info &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "FilsRealmInfo",
+			    wpabuf_head(anqp->fils_realm_info),
+			    wpabuf_len(anqp->fils_realm_info)))
+			goto nomem;
+
+#ifdef CONFIG_HS20
+		if (anqp->hs20_capability_list &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20CapabilityList",
+			    wpabuf_head(anqp->hs20_capability_list),
+			    wpabuf_len(anqp->hs20_capability_list)))
+			goto nomem;
+		if (anqp->hs20_operator_friendly_name &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20OperatorFriendlyName",
+			    wpabuf_head(anqp->hs20_operator_friendly_name),
+			    wpabuf_len(anqp->hs20_operator_friendly_name)))
+			goto nomem;
+		if (anqp->hs20_wan_metrics &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20WanMetrics",
+			    wpabuf_head(anqp->hs20_wan_metrics),
+			    wpabuf_len(anqp->hs20_wan_metrics)))
+			goto nomem;
+		if (anqp->hs20_connection_capability &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20ConnectionCapability",
+			    wpabuf_head(anqp->hs20_connection_capability),
+			    wpabuf_len(anqp->hs20_connection_capability)))
+			goto nomem;
+		if (anqp->hs20_operating_class &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20OperatingClass",
+			    wpabuf_head(anqp->hs20_operating_class),
+			    wpabuf_len(anqp->hs20_operating_class)))
+			goto nomem;
+		if (anqp->hs20_osu_providers_list &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20OSUProvidersList",
+			    wpabuf_head(anqp->hs20_osu_providers_list),
+			    wpabuf_len(anqp->hs20_osu_providers_list)))
+			goto nomem;
+		if (anqp->hs20_operator_icon_metadata &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20OperatorIconMetadata",
+			    wpabuf_head(anqp->hs20_operator_icon_metadata),
+			    wpabuf_len(anqp->hs20_operator_icon_metadata)))
+			goto nomem;
+		if (anqp->hs20_osu_providers_nai_list &&
+		    !wpa_dbus_dict_append_byte_array(
+			    &iter_dict, "HS20OSUProvidersNAIList",
+			    wpabuf_head(anqp->hs20_osu_providers_nai_list),
+			    wpabuf_len(anqp->hs20_osu_providers_nai_list)))
+			goto nomem;
+#endif /* CONFIG_HS20 */
+
+		dl_list_for_each(elem, &anqp->anqp_elems,
+				 struct wpa_bss_anqp_elem, list) {
+			char title[32];
+
+			os_snprintf(title, sizeof(title), "anqp[%u]",
+				    elem->infoid);
+			if (!wpa_dbus_dict_append_byte_array(
+				    &iter_dict, title,
+				    wpabuf_head(elem->payload),
+				    wpabuf_len(elem->payload)))
+				goto nomem;
+
+			os_snprintf(title, sizeof(title),
+				    "protected-anqp-info[%u]", elem->infoid);
+			if (!wpa_dbus_dict_append_bool(
+				    &iter_dict, title,
+				    elem->protected_response))
+				goto nomem;
+		}
+#endif /* CONFIG_INTERWORKING */
+	}
+
+	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
+		goto nomem;
+
+	return TRUE;
+
+nomem:
+	dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
+	return FALSE;
+}
+
+
+/**
  * wpas_dbus_getter_enabled - Check whether network is enabled or disabled
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 97fa337..acd6af7 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -157,6 +157,9 @@
 wpas_dbus_handler_interworking_select(DBusMessage *message,
 				      struct wpa_supplicant *wpa_s);
 
+DBusMessage *
+wpas_dbus_handler_anqp_get(DBusMessage *message, struct wpa_supplicant *wpa_s);
+
 DECLARE_ACCESSOR(wpas_dbus_getter_capabilities);
 DECLARE_ACCESSOR(wpas_dbus_getter_state);
 DECLARE_ACCESSOR(wpas_dbus_getter_scanning);
@@ -216,6 +219,7 @@
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps);
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies);
 DECLARE_ACCESSOR(wpas_dbus_getter_bss_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_anqp);
 DECLARE_ACCESSOR(wpas_dbus_getter_enabled);
 DECLARE_ACCESSOR(wpas_dbus_setter_enabled);
 DECLARE_ACCESSOR(wpas_dbus_getter_network_properties);
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 801e698..e223450 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -238,6 +238,12 @@
 		wait_time = wpa_s->dpp_resp_retry_time;
 	else
 		wait_time = 1000;
+	if (wpa_s->dpp_tx_chan_change) {
+		wpa_s->dpp_tx_chan_change = false;
+		if (wait_time > 100)
+			wait_time = 100;
+	}
+
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Schedule retransmission of Authentication Response frame in %u ms",
 		wait_time);
@@ -487,6 +493,21 @@
 }
 
 
+static void wpas_dpp_neg_freq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!wpa_s->dpp_listen_on_tx_expire || !auth || !auth->neg_freq)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Start listen on neg_freq %u MHz based on timeout for TX wait expiration",
+		   auth->neg_freq);
+	wpas_dpp_listen_start(wpa_s, auth->neg_freq);
+}
+
+
 static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
 			       unsigned int freq, const u8 *dst,
 			       const u8 *src, const u8 *bssid,
@@ -605,7 +626,9 @@
 			   wpa_s->dpp_auth->curr_freq,
 			   wpa_s->dpp_auth->neg_freq);
 		offchannel_send_action_done(wpa_s);
-		wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->neg_freq);
+		wpa_s->dpp_listen_on_tx_expire = true;
+		eloop_register_timeout(0, 100000, wpas_dpp_neg_freq_timeout,
+				       wpa_s, NULL);
 	}
 
 	if (wpa_s->dpp_auth_ok_on_ack)
@@ -1035,6 +1058,8 @@
 	wpa_s->off_channel_freq = 0;
 	wpa_s->roc_waiting_drv_freq = lwork->freq;
 	wpa_drv_dpp_listen(wpa_s, true);
+	wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+	wpa_s->dpp_tx_chan_change = false;
 }
 
 
@@ -1134,11 +1159,58 @@
 }
 
 
+static void wpas_dpp_tx_auth_resp(struct wpa_supplicant *wpa_s)
+{
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth)
+		return;
+
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+		MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+		DPP_PA_AUTHENTICATION_RESP);
+	offchannel_send_action(wpa_s, auth->curr_freq,
+			       auth->peer_mac_addr, wpa_s->own_addr, broadcast,
+			       wpabuf_head(auth->resp_msg),
+			       wpabuf_len(auth->resp_msg),
+			       500, wpas_dpp_tx_status, 0);
+}
+
+
+static void wpas_dpp_tx_auth_resp_roc_timeout(void *eloop_ctx,
+					      void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth || !wpa_s->dpp_tx_auth_resp_on_roc_stop)
+		return;
+
+	wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+	wpa_s->dpp_tx_chan_change = true;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Send postponed Authentication Response on remain-on-channel termination timeout");
+	wpas_dpp_tx_auth_resp(wpa_s);
+}
+
+
 void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 					  unsigned int freq)
 {
+	wpa_printf(MSG_DEBUG, "DPP: Remain on channel cancel for %u MHz", freq);
 	wpas_dpp_listen_work_done(wpa_s);
 
+	if (wpa_s->dpp_auth && wpa_s->dpp_tx_auth_resp_on_roc_stop) {
+		eloop_cancel_timeout(wpas_dpp_tx_auth_resp_roc_timeout,
+				     wpa_s, NULL);
+		wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+		wpa_s->dpp_tx_chan_change = true;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Send postponed Authentication Response on remain-on-channel termination");
+		wpas_dpp_tx_auth_resp(wpa_s);
+		return;
+	}
+
 	if (wpa_s->dpp_auth && wpa_s->dpp_in_response_listen) {
 		unsigned int new_freq;
 
@@ -1256,17 +1328,17 @@
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Stop listen on %u MHz to allow response on the request %u MHz",
 			   wpa_s->dpp_listen_freq, wpa_s->dpp_auth->curr_freq);
+		wpa_s->dpp_tx_auth_resp_on_roc_stop = true;
+		eloop_register_timeout(0, 100000,
+				       wpas_dpp_tx_auth_resp_roc_timeout,
+				       wpa_s, NULL);
 		wpas_dpp_listen_stop(wpa_s);
+		return;
 	}
+	wpa_s->dpp_tx_auth_resp_on_roc_stop = false;
+	wpa_s->dpp_tx_chan_change = false;
 
-	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
-		MAC2STR(src), wpa_s->dpp_auth->curr_freq,
-		DPP_PA_AUTHENTICATION_RESP);
-	offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
-			       src, wpa_s->own_addr, broadcast,
-			       wpabuf_head(wpa_s->dpp_auth->resp_msg),
-			       wpabuf_len(wpa_s->dpp_auth->resp_msg),
-			       500, wpas_dpp_tx_status, 0);
+	wpas_dpp_tx_auth_resp(wpa_s);
 }
 
 
@@ -1275,6 +1347,15 @@
 	struct dpp_authentication *auth = wpa_s->dpp_auth;
 	int freq;
 
+	if (wpa_s->dpp_listen_on_tx_expire && auth && auth->neg_freq) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Start listen on neg_freq %u MHz based on TX wait expiration on the previous channel",
+			   auth->neg_freq);
+		eloop_cancel_timeout(wpas_dpp_neg_freq_timeout, wpa_s, NULL);
+		wpas_dpp_listen_start(wpa_s, auth->neg_freq);
+		return;
+	}
+
 	if (!wpa_s->dpp_gas_server || !auth) {
 		if (auth && auth->waiting_auth_resp &&
 		    eloop_is_timeout_registered(wpas_dpp_drv_wait_timeout,
@@ -4816,6 +4897,8 @@
 	eloop_cancel_timeout(wpas_dpp_gas_initial_resp_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_dpp_gas_client_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_dpp_drv_wait_timeout, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_dpp_tx_auth_resp_roc_timeout, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_dpp_neg_freq_timeout, wpa_s, NULL);
 #ifdef CONFIG_DPP2
 	eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout,
@@ -5627,6 +5710,7 @@
 	eloop_register_timeout(100, 0, wpas_dpp_push_button_expire,
 			       wpa_s, NULL);
 
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS "started");
 	return 0;
 }
 
@@ -5688,6 +5772,7 @@
 	wpa_s->scan_req = MANUAL_SCAN_REQ;
 	wpa_s->scan_res_handler = wpas_dpp_pb_scan_res_handler;
 	wpa_supplicant_req_scan(wpa_s, 0, 0);
+	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS "started");
 	return 0;
 }
 
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 9a4c235..d01b52b 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -128,7 +128,7 @@
 }
 
 struct wpa_scan_results *
-wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s);
+wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s, const u8 *bssid);
 
 static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid)
 {
@@ -342,14 +342,6 @@
 	return -1;
 }
 
-static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
-				 struct wpa_driver_ap_params *params)
-{
-	if (wpa_s->driver->set_ap)
-		return wpa_s->driver->set_ap(wpa_s->drv_priv, params);
-	return -1;
-}
-
 static inline int wpa_drv_sta_add(struct wpa_supplicant *wpa_s,
 				  struct hostapd_sta_add_params *params)
 {
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 9641062..95953de 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -670,6 +670,10 @@
 	wpa_s->eapol = NULL;
 	if (e->radius_conf && e->radius_conf->auth_server) {
 		os_free(e->radius_conf->auth_server->shared_secret);
+		os_free(e->radius_conf->auth_server->ca_cert);
+		os_free(e->radius_conf->auth_server->client_cert);
+		os_free(e->radius_conf->auth_server->private_key);
+		os_free(e->radius_conf->auth_server->private_key_passwd);
 		os_free(e->radius_conf->auth_server);
 	}
 	os_free(e->radius_conf);
@@ -1007,7 +1011,10 @@
 
 static void wpa_init_conf(struct eapol_test_data *e,
 			  struct wpa_supplicant *wpa_s, const char *authsrv,
-			  int port, const char *secret,
+			  int port, bool tls, const char *secret,
+			  const char *ca_cert, const char *client_cert,
+			  const char *private_key,
+			  const char *private_key_passwd,
 			  const char *cli_addr, const char *ifname)
 {
 	struct hostapd_radius_server *as;
@@ -1045,8 +1052,17 @@
 	}
 #endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
 	as->port = port;
+	as->tls = tls;
 	as->shared_secret = (u8 *) os_strdup(secret);
 	as->shared_secret_len = os_strlen(secret);
+	if (ca_cert)
+		as->ca_cert = os_strdup(ca_cert);
+	if (client_cert)
+		as->client_cert = os_strdup(client_cert);
+	if (private_key)
+		as->private_key = os_strdup(private_key);
+	if (private_key_passwd)
+		as->private_key_passwd = os_strdup(private_key_passwd);
 	e->radius_conf->auth_server = as;
 	e->radius_conf->auth_servers = as;
 	e->radius_conf->msg_dumps = 1;
@@ -1256,11 +1272,16 @@
 {
 	printf("usage:\n"
 	       "eapol_test [-enWSv] -c<conf> [-a<AS IP>] [-p<AS port>] "
-	       "[-s<AS secret>]\\\n"
+	       "[-s<AS secret>] \\\n"
+	       "           [-X<RADIUS protocol> \\\n"
 	       "           [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
 	       "           [-M<client MAC address>] [-o<server cert file] \\\n"
 	       "           [-N<attr spec>] [-R<PC/SC reader>] "
 	       "[-P<PC/SC PIN>] \\\n"
+#ifdef CONFIG_RADIUS_TLS
+	       "           [-j<CA cert>] [-J<client cert>] \\\n"
+	       "[-k<private key] [-K<private key passwd>] \\\n"
+#endif /* CONFIG_RADIUS_TLS */
 	       "           [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
 	       "eapol_test scard\n"
 	       "eapol_test sim <PIN> <num triplets> [debug]\n"
@@ -1269,10 +1290,11 @@
 	       "  -c<conf> = configuration file\n"
 	       "  -a<AS IP> = IP address of the authentication server, "
 	       "default 127.0.0.1\n"
-	       "  -p<AS port> = UDP port of the authentication server, "
-	       "default 1812\n"
-	       "  -s<AS secret> = shared secret with the authentication "
-	       "server, default 'radius'\n"
+	       "  -p<AS port> = Port of the authentication server,\n"
+	       "                default 1812 for RADIUS/UDP and 2083 for RADIUS/TLS\n"
+	       "  -s<AS secret> = shared secret with the authentication server,\n"
+	       "                  default 'radius' for RADIUS/UDP and 'radsec' for RADIUS/TLS\n"
+	       "  -X<RADIUS protocol> = RADIUS protocol to use: UDP (default) or TLS\n"
 	       "  -A<client IP> = IP address of the client, default: select "
 	       "automatically\n"
 	       "  -r<count> = number of re-authentications\n"
@@ -1310,8 +1332,11 @@
 	struct wpa_supplicant wpa_s;
 	int c, ret = 1, wait_for_monitor = 0, save_config = 0;
 	char *as_addr = "127.0.0.1";
-	int as_port = 1812;
-	char *as_secret = "radius";
+	int as_port = -1;
+	char *as_secret = NULL;
+	char *ca_cert = NULL, *client_cert = NULL;
+	char *private_key = NULL, *private_key_passwd = NULL;
+	bool tls = false;
 	char *cli_addr = NULL;
 	char *conf = NULL;
 	int timeout = 30;
@@ -1334,7 +1359,8 @@
 	wpa_debug_show_keys = 1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW");
+		c = getopt(argc, argv,
+			   "a:A:c:C:ei:j:J:k:K:M:nN:o:p:P:r:R:s:St:T:vWX:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -1356,6 +1382,20 @@
 		case 'i':
 			ifname = optarg;
 			break;
+#ifdef CONFIG_RADIUS_TLS
+		case 'j':
+			ca_cert = optarg;
+			break;
+		case 'J':
+			client_cert = optarg;
+			break;
+		case 'k':
+			private_key = optarg;
+			break;
+		case 'K':
+			private_key_passwd = optarg;
+			break;
+#endif /* CONFIG_RADIUS_TLS */
 		case 'M':
 			if (hwaddr_aton(optarg, eapol_test.own_addr)) {
 				usage();
@@ -1406,6 +1446,16 @@
 		case 'W':
 			wait_for_monitor++;
 			break;
+		case 'X':
+			if (os_strcmp(optarg, "UDP") == 0) {
+				tls = false;
+			} else if (os_strcmp(optarg, "TLS") == 0) {
+				tls = true;
+			} else {
+				usage();
+				return -1;
+			}
+			break;
 		case 'N':
 			p1 = os_zalloc(sizeof(*p1));
 			if (p1 == NULL)
@@ -1440,6 +1490,11 @@
 		}
 	}
 
+	if (!as_secret)
+		as_secret = tls ? "radsec" : "radius";
+	if (as_port < 0)
+		as_port = tls ? 2083 : 1812;
+
 	if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
 		return scard_test(&eapol_test);
 	}
@@ -1489,7 +1544,8 @@
 		wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader);
 	}
 
-	wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
+	wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, tls, as_secret,
+		      ca_cert, client_cert, private_key, private_key_passwd,
 		      cli_addr, ifname);
 	wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
 	if (wpa_s.ctrl_iface == NULL) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index bc45579..ff18543 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -164,7 +164,7 @@
 	struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
 
 	if (!bss) {
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, bssid);
 
 		/* Get the BSS from the new scan results */
 		bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
@@ -183,7 +183,7 @@
 	struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
 
 	if (!bss) {
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, bssid);
 		bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
 	}
 
@@ -1156,19 +1156,18 @@
 
 
 static bool wpas_valid_ml_bss(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
-
 {
 	u16 removed_links;
 
 	if (wpa_bss_parse_basic_ml_element(wpa_s, bss, NULL, NULL, NULL, NULL))
 		return true;
 
-	if (bss->n_mld_links == 0)
+	if (!bss->valid_links)
 		return true;
 
 	/* Check if the current BSS is going to be removed */
 	removed_links = wpa_bss_parse_reconf_ml_element(wpa_s, bss);
-	if (BIT(bss->mld_links[0].link_id) & removed_links)
+	if (BIT(bss->mld_link_id) & removed_links)
 		return false;
 
 	return true;
@@ -1697,13 +1696,11 @@
 		return NULL;
 	}
 
-#ifdef CONFIG_WNM
 	if (wnm_is_bss_excluded(wpa_s, bss)) {
 		if (debug_print)
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID excluded");
 		return NULL;
 	}
-#endif /* CONFIG_WNM */
 
 	for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
 		if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
@@ -1809,10 +1806,12 @@
 				break;
 		}
 
-		if (selected == NULL && wpa_s->bssid_ignore &&
+		if (!selected &&
+		    (wpa_s->bssid_ignore || wnm_active_bss_trans_mgmt(wpa_s)) &&
 		    !wpa_s->countermeasures) {
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"No APs found - clear BSSID ignore list and try again");
+			wnm_btm_reset(wpa_s);
 			wpa_bssid_ignore_clear(wpa_s);
 			wpa_s->bssid_ignore_cleared = true;
 		} else if (selected == NULL)
@@ -2409,7 +2408,7 @@
 
 	scan_res = wpa_supplicant_get_scan_results(wpa_s,
 						   data ? &data->scan_info :
-						   NULL, 1);
+						   NULL, 1, NULL);
 	if (scan_res == NULL) {
 		if (wpa_s->conf->ap_scan == 2 || ap ||
 		    wpa_s->scan_res_handler == scan_only_handler)
@@ -2498,7 +2497,7 @@
 		return 0;
 	}
 
-	if (wnm_scan_process(wpa_s, 1) > 0)
+	if (wnm_scan_process(wpa_s, false) > 0)
 		goto scan_work_done;
 
 	if (sme_proc_obss_scan(wpa_s) > 0)
@@ -2941,8 +2940,6 @@
 }
 
 
-#ifdef CONFIG_INTERWORKING
-
 static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
 			    size_t len)
 {
@@ -2975,8 +2972,6 @@
 	}
 }
 
-#endif /* CONFIG_INTERWORKING */
-
 
 static void wpa_supplicant_set_4addr_mode(struct wpa_supplicant *wpa_s)
 {
@@ -3002,25 +2997,24 @@
 					const u8 *ies, size_t ies_len)
 {
 	struct ieee802_11_elems elems;
-	const u8 *map_sub_elem, *pos;
-	size_t len;
+	struct multi_ap_params multi_ap;
+	u16 status;
 
 	wpa_s->multi_ap_ie = 0;
 
 	if (!ies ||
 	    ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed ||
-	    !elems.multi_ap || elems.multi_ap_len < 7)
+	    !elems.multi_ap)
 		return;
 
-	pos = elems.multi_ap + 4;
-	len = elems.multi_ap_len - 4;
-
-	map_sub_elem = get_ie(pos, len, MULTI_AP_SUB_ELEM_TYPE);
-	if (!map_sub_elem || map_sub_elem[1] < 1)
+	status = check_multi_ap_ie(elems.multi_ap + 4, elems.multi_ap_len - 4,
+				   &multi_ap);
+	if (status != WLAN_STATUS_SUCCESS)
 		return;
 
-	wpa_s->multi_ap_backhaul = !!(map_sub_elem[2] & MULTI_AP_BACKHAUL_BSS);
-	wpa_s->multi_ap_fronthaul = !!(map_sub_elem[2] &
+	wpa_s->multi_ap_backhaul = !!(multi_ap.capability &
+				      MULTI_AP_BACKHAUL_BSS);
+	wpa_s->multi_ap_fronthaul = !!(multi_ap.capability &
 				       MULTI_AP_FRONTHAUL_BSS);
 	wpa_s->multi_ap_ie = 1;
 }
@@ -3359,10 +3353,8 @@
 		wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
 				       data->assoc_info.resp_ies_len);
 #endif /* CONFIG_WNM */
-#ifdef CONFIG_INTERWORKING
 		interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
 						data->assoc_info.resp_ies_len);
-#endif /* CONFIG_INTERWORKING */
 		if (wpa_s->hw_capab == CAPAB_VHT &&
 		    get_ie(data->assoc_info.resp_ies,
 			   data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
@@ -3718,12 +3710,21 @@
 	if (wpa_found || rsn_found)
 		wpa_s->ap_ies_from_associnfo = 1;
 
-	if (wpa_s->assoc_freq && data->assoc_info.freq &&
-	    wpa_s->assoc_freq != data->assoc_info.freq) {
-		wpa_printf(MSG_DEBUG, "Operating frequency changed from "
-			   "%u to %u MHz",
-			   wpa_s->assoc_freq, data->assoc_info.freq);
-		wpa_supplicant_update_scan_results(wpa_s);
+	if (wpa_s->assoc_freq && data->assoc_info.freq) {
+		struct wpa_bss *bss;
+		unsigned int freq = 0;
+
+		if (bssid_known) {
+			bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+			if (bss)
+				freq = bss->freq;
+		}
+		if (freq != data->assoc_info.freq) {
+			wpa_printf(MSG_DEBUG,
+				   "Operating frequency changed from %u to %u MHz",
+				   wpa_s->assoc_freq, data->assoc_info.freq);
+			wpa_supplicant_update_scan_results(wpa_s, bssid);
+		}
 	}
 
 	wpa_s->assoc_freq = data->assoc_info.freq;
@@ -4077,10 +4078,7 @@
 		if (!mlo.valid_links)
 			return 0;
 
-		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-			if (!(mlo.valid_links & BIT(i)))
-				continue;
-
+		for_each_link(mlo.valid_links, i) {
 			if (!ether_addr_equal(wpa_s->links[i].addr,
 					      mlo.links[i].addr) ||
 			    !ether_addr_equal(wpa_s->links[i].bssid,
@@ -4098,10 +4096,7 @@
 	wpa_s->valid_links = mlo.valid_links;
 	wpa_s->mlo_assoc_link_id = mlo.assoc_link_id;
 	os_memcpy(wpa_s->ap_mld_addr, mlo.ap_mld_addr, ETH_ALEN);
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		os_memcpy(wpa_s->links[i].addr, mlo.links[i].addr, ETH_ALEN);
 		os_memcpy(wpa_s->links[i].bssid, mlo.links[i].bssid, ETH_ALEN);
 		wpa_s->links[i].freq = mlo.links[i].freq;
@@ -4134,15 +4129,13 @@
 	wpa_mlo.valid_links = drv_mlo.valid_links;
 	wpa_mlo.req_links = drv_mlo.req_links;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+	for_each_link(drv_mlo.req_links, i) {
 		struct wpa_bss *bss;
 
-		if (!(drv_mlo.req_links & BIT(i)))
-			continue;
-
 		bss = wpa_supplicant_get_new_bss(wpa_s, drv_mlo.links[i].bssid);
 		if (!bss) {
-			wpa_supplicant_update_scan_results(wpa_s);
+			wpa_supplicant_update_scan_results(
+				wpa_s, drv_mlo.links[i].bssid);
 			bss = wpa_supplicant_get_new_bss(
 				wpa_s, drv_mlo.links[i].bssid);
 		}
@@ -6021,13 +6014,10 @@
 	pos += res;
 
 	if (!info->default_map) {
-		for (i = 0; i < MAX_NUM_MLD_LINKS && end > pos; i++) {
+		for_each_link(info->valid_links, i) {
 			char uplink_map_str[9];
 			char downlink_map_str[9];
 
-			if (!(info->valid_links & BIT(i)))
-				continue;
-
 			bitmap_to_str(info->t2lmap[i].uplink, uplink_map_str);
 			bitmap_to_str(info->t2lmap[i].downlink,
 				      downlink_map_str);
@@ -6307,6 +6297,13 @@
 			" type=%d stype=%d",
 			MAC2STR(data->tx_status.dst),
 			data->tx_status.type, data->tx_status.stype);
+#ifdef CONFIG_WNM
+		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+		    data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
+		    wnm_btm_resp_tx_status(wpa_s, data->tx_status.data,
+					   data->tx_status.data_len) == 0)
+			break;
+#endif /* CONFIG_WNM */
 #ifdef CONFIG_PASN
 		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
 		    data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 498cc98..5676f38 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -2820,7 +2820,7 @@
 	struct wpa_bss *bss;
 	int res;
 
-	bss = wpa_bss_get_bssid(wpa_s, dst);
+	bss = wpa_bss_get_bssid_latest(wpa_s, dst);
 	if (!bss && !freq) {
 		wpa_printf(MSG_WARNING,
 			   "ANQP: Cannot send query without BSS freq info");
@@ -3175,7 +3175,7 @@
 		}
 	}
 	if (bss == NULL)
-		bss = wpa_bss_get_bssid(wpa_s, dst);
+		bss = wpa_bss_get_bssid_latest(wpa_s, dst);
 
 	pos = wpabuf_head(resp);
 	end = pos + wpabuf_len(resp);
@@ -3206,12 +3206,12 @@
 	}
 
 out_parse_done:
+	if (bss)
+		wpas_notify_bss_anqp_changed(wpa_s, bss->id);
 #ifdef CONFIG_HS20
 	hs20_notify_parse_done(wpa_s);
 #endif /* CONFIG_HS20 */
 out:
-	wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
-		MAC2STR(dst), anqp_result);
 	wpas_notify_anqp_query_done(wpa_s, dst, anqp_result, bss ? bss->anqp : NULL);
 }
 
@@ -3290,7 +3290,7 @@
 	u8 query_resp_len_limit = 0;
 
 	freq = wpa_s->assoc_freq;
-	bss = wpa_bss_get_bssid(wpa_s, dst);
+	bss = wpa_bss_get_bssid_latest(wpa_s, dst);
 	if (bss)
 		freq = bss->freq;
 	if (freq <= 0)
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 486fc6a..85c1ea8 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -231,7 +231,7 @@
 		    hostapd_get_oper_centr_freq_seg0_idx(ifmsh->conf),
 		    hostapd_get_oper_centr_freq_seg1_idx(ifmsh->conf),
 		    ifmsh->conf->vht_capab,
-		    he_capab, NULL)) {
+		    he_capab, NULL, 0)) {
 		wpa_printf(MSG_ERROR, "Error updating mesh frequency params");
 		wpa_supplicant_mesh_deinit(wpa_s, true);
 		return -1;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 6f162f2..c930651 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -627,6 +627,15 @@
 }
 
 
+void wpas_notify_bss_anqp_changed(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_ANQP, id);
+}
+
+
 void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
 {
 	if (wpa_s->p2p_mgmt)
@@ -935,7 +944,8 @@
 				const u8 *p2p_dev_addr, const u8 *ip)
 {
 	if (authorized)
-		wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr, ip);
+		wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr,
+					      ip);
 	else
 		wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr);
 }
@@ -1057,11 +1067,14 @@
 				 const char *result,
 				 const struct wpa_bss_anqp *anqp)
 {
+	wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
+		MAC2STR(bssid), result);
 #ifdef CONFIG_INTERWORKING
 	if (!wpa_s || !bssid || !anqp)
 		return;
 
 	wpas_aidl_notify_anqp_query_done(wpa_s, bssid, result, anqp);
+	wpas_dbus_signal_anqp_query_done(wpa_s, bssid, result);
 #endif /* CONFIG_INTERWORKING */
 }
 
@@ -1359,6 +1372,7 @@
 	wpas_dbus_signal_interworking_select_done(wpa_s);
 }
 
+
 #endif /* CONFIG_INTERWORKING */
 
 void wpas_notify_eap_method_selected(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index d4656ad..a584884 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -89,6 +89,8 @@
 void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
 				   unsigned int id);
 void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id);
+void wpas_notify_bss_anqp_changed(struct wpa_supplicant *wpa_s,
+				  unsigned int id);
 void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name);
 void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name);
 
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
index b4ad3ca..ff11d20 100644
--- a/wpa_supplicant/op_classes.c
+++ b/wpa_supplicant/op_classes.c
@@ -22,13 +22,12 @@
 				       unsigned int *flags)
 {
 	int i;
-	bool is_6ghz = op_class >= 131 && op_class <= 136;
+	bool is_6ghz = is_6ghz_op_class(op_class);
 
 	for (i = 0; i < mode->num_channels; i++) {
 		bool chan_is_6ghz;
 
-		chan_is_6ghz = mode->channels[i].freq >= 5935 &&
-			mode->channels[i].freq <= 7115;
+		chan_is_6ghz = is_6ghz_freq(mode->channels[i].freq);
 		if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan)
 			break;
 	}
@@ -187,6 +186,69 @@
 }
 
 
+static int get_center_320mhz(struct hostapd_hw_modes *mode, u8 channel,
+			     const u8 *center_channels, size_t num_chan)
+{
+	unsigned int i;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A || !mode->is_6ghz)
+		return 0;
+
+	for (i = 0; i < num_chan; i++) {
+		/*
+		* In 320 MHz, the bandwidth "spans" 60 channels (e.g., 65-125),
+		* so the center channel is 30 channels away from the start/end.
+		*/
+		if (channel >= center_channels[i] - 30 &&
+		    channel <= center_channels[i] + 30)
+			return center_channels[i];
+	}
+
+	return 0;
+}
+
+
+static enum chan_allowed verify_320mhz(struct hostapd_hw_modes *mode,
+				       u8 op_class, u8 channel)
+{
+	u8 center_chan;
+	unsigned int i;
+	bool no_ir = false;
+	const u8 *center_channels;
+	size_t num_chan;
+	const u8 center_channels_6ghz[] = { 31, 63, 95, 127, 159, 191 };
+
+	center_channels = center_channels_6ghz;
+	num_chan = ARRAY_SIZE(center_channels_6ghz);
+
+	center_chan = get_center_320mhz(mode, channel, center_channels,
+					num_chan);
+	if (!center_chan)
+		return NOT_ALLOWED;
+
+	/* Check all the channels are available */
+	for (i = 0; i < 16; i++) {
+		unsigned int flags;
+		u8 adj_chan = center_chan - 30 + i * 4;
+
+		if (allow_channel(mode, op_class, adj_chan, &flags) ==
+		    NOT_ALLOWED)
+			return NOT_ALLOWED;
+
+		if (!(flags & HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL))
+			return NOT_ALLOWED;
+
+		if (flags & HOSTAPD_CHAN_NO_IR)
+			no_ir = true;
+	}
+
+	if (no_ir)
+		return NO_IR;
+
+	return ALLOWED;
+}
+
+
 enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class,
 				 u8 channel, u8 bw)
 {
@@ -228,6 +290,13 @@
 		 * result and use only the 80 MHz specific version.
 		 */
 		res2 = res = verify_80mhz(mode, op_class, channel);
+	} else if (bw == BW320) {
+		/*
+		 * channel is a center channel and as such, not necessarily a
+		 * valid 20 MHz channels. Override earlier allow_channel()
+		 * result and use only the 320 MHz specific version.
+		 */
+		res2= res = verify_320mhz(mode, op_class, channel);
 	}
 
 	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
@@ -251,6 +320,7 @@
 	int z;
 	int freq2 = 0;
 	int freq5 = 0;
+	bool freq6 = false;
 
 	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode,
 			is_6ghz_op_class(op_class->op_class));
@@ -265,7 +335,9 @@
 
 			if (f == 0)
 				break; /* end of list */
-			if (f > 4000 && f < 6000)
+			if (is_6ghz_freq(f))
+				freq6 = true;
+			else if (f > 4000 && f < 6000)
 				freq5 = 1;
 			else if (f > 2400 && f < 2500)
 				freq2 = 1;
@@ -274,8 +346,11 @@
 		/* No frequencies specified, can use anything hardware supports.
 		 */
 		freq2 = freq5 = 1;
+		freq6 = true;
 	}
 
+	if (is_6ghz_op_class(op_class->op_class) && !freq6)
+		return 0;
 	if (op_class->op_class >= 115 && op_class->op_class <= 130 && !freq5)
 		return 0;
 	if (op_class->op_class >= 81 && op_class->op_class <= 84 && !freq2)
@@ -453,6 +528,7 @@
 	u8 op, current, chan;
 	u8 *ie_len;
 	size_t res;
+	bool op128 = false, op130 = false, op133 = false, op135 = false;
 
 	/*
 	 * Determine the current operating class correct mode based on
@@ -480,8 +556,50 @@
 	wpabuf_put_u8(buf, current);
 
 	for (op = 0; global_op_class[op].op_class; op++) {
-		if (wpas_op_class_supported(wpa_s, ssid, &global_op_class[op]))
-			wpabuf_put_u8(buf, global_op_class[op].op_class);
+		bool supp;
+		u8 op_class = global_op_class[op].op_class;
+
+		supp = wpas_op_class_supported(wpa_s, ssid,
+					       &global_op_class[op]);
+		if (!supp)
+			continue;
+		switch (op_class) {
+		case 128:
+			op128 = true;
+			break;
+		case 130:
+			op130 = true;
+			break;
+		case 133:
+			op133 = true;
+			break;
+		case 135:
+			op135 = true;
+			break;
+		}
+		if (is_80plus_op_class(op_class))
+			continue;
+
+		/* Add a 1-octet operating class to the Operating Class field */
+		wpabuf_put_u8(buf, global_op_class[op].op_class);
+	}
+
+	/* Add the 2-octet operating classes (i.e., 80+80 MHz cases), if any */
+	if ((op128 && op130) || (op133 && op135)) {
+		/* Operating Class Duple Sequence field */
+
+		/* Zero Delimiter */
+		wpabuf_put_u8(buf, 0);
+
+		/* Operating Class Duple List */
+		if (op128 && op130) {
+			wpabuf_put_u8(buf, 130);
+			wpabuf_put_u8(buf, 128);
+		}
+		if (op133 && op135) {
+			wpabuf_put_u8(buf, 135);
+			wpabuf_put_u8(buf, 133);
+		}
 	}
 
 	*ie_len = wpabuf_len(buf) - 2;
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 3c121c8..9c20ee5 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1103,6 +1103,8 @@
 
 	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
 
+	wpa_s->p2p_go_no_pri_sec_switch = 0;
+
 	return 0;
 }
 
@@ -7003,7 +7005,8 @@
 			return -1;
 	}
 
-	if (wpas_p2p_init_go_params(wpa_s, &params, selected_freq, vht_center_freq2,
+	if (wpas_p2p_init_go_params(wpa_s, &params, selected_freq,
+				    vht_center_freq2,
 				    ht40, vht, max_oper_chwidth, he, edmg,
 				    NULL))
 		return -1;
@@ -7097,7 +7100,7 @@
 	 * fetch time on the same radio so it reflects the actual time the last
 	 * scan result event occurred.
 	 */
-	wpa_supplicant_update_scan_results(wpa_s);
+	wpa_supplicant_update_scan_results(wpa_s, go_bssid);
 	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
 			 radio_list) {
 		if (ifs == wpa_s)
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 2e65cf0..1bb38f7 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -166,7 +166,7 @@
 
 	bss = wpa_bss_get_bssid(wpa_s, peer_addr);
 	if (!bss) {
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, peer_addr);
 		bss = wpa_bss_get_bssid(wpa_s, peer_addr);
 		if (!bss) {
 			wpa_printf(MSG_DEBUG, "PASN: BSS not found");
@@ -560,9 +560,10 @@
 		derive_kdk = wpa_s->conf->force_kdk_derivation;
 #endif /* CONFIG_TESTING_OPTIONS */
 	if (derive_kdk)
-		pasn->kdk_len = WPA_KDK_MAX_LEN;
+		pasn_enable_kdk_derivation(pasn);
 	else
-		pasn->kdk_len = 0;
+		pasn_disable_kdk_derivation(pasn);
+
 	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
 
 	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA) &&
@@ -582,9 +583,8 @@
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
 	if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
 		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
-	pasn->rsnxe_capab = capab;
-	pasn->send_mgmt = wpas_pasn_send_mlme;
-
+	pasn_set_rsnxe_caps(pasn, capab);
+	pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
 	ssid = wpa_config_get_network(wpa_s->conf, awork->network_id);
 
 #ifdef CONFIG_SAE
@@ -594,7 +594,7 @@
 				   "PASN: No network profile found for SAE");
 			goto fail;
 		}
-		pasn->pt = wpas_pasn_sae_derive_pt(ssid, awork->group);
+		pasn_set_pt(pasn, wpas_pasn_sae_derive_pt(ssid, awork->group));
 		if (!pasn->pt) {
 			wpa_printf(MSG_DEBUG, "PASN: Failed to derive PT");
 			goto fail;
@@ -629,8 +629,7 @@
 	}
 #endif /* CONFIG_FILS */
 
-	pasn->cb_ctx = wpa_s;
-	pasn->pmksa = wpa_sm_get_pmksa_cache(wpa_s->wpa);
+	pasn_set_initiator_pmksa(pasn, wpa_sm_get_pmksa_cache(wpa_s->wpa));
 
 	if (wpa_key_mgmt_ft(awork->akmp)) {
 #ifdef CONFIG_IEEE80211R
@@ -753,7 +752,8 @@
 
 	wpa_printf(MSG_DEBUG, "PASN: Stopping authentication");
 
-	wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn->akmp, pasn->cipher,
+	wpas_pasn_auth_status(wpa_s, pasn->peer_addr, pasn_get_akmp(pasn),
+			      pasn_get_cipher(pasn),
 			      pasn->status, pasn->comeback,
 			      pasn->comeback_after);
 
@@ -765,8 +765,8 @@
 				     struct pasn_data *pasn,
 				     struct wpa_pasn_params_data *params)
 {
-	int akmp = pasn->akmp;
-	int cipher = pasn->cipher;
+	int akmp = pasn_get_akmp(pasn);
+	int cipher = pasn_get_cipher(pasn);
 	u16 group = pasn->group;
 	u8 own_addr[ETH_ALEN];
 	u8 peer_addr[ETH_ALEN];
@@ -806,20 +806,22 @@
 	if (!wpa_s->pasn_auth_work)
 		return -2;
 
-	pasn->cb_ctx = wpa_s;
+	pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
 	ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
 	if (ret == 0) {
 		ptksa_cache_add(wpa_s->ptksa, pasn->own_addr, pasn->peer_addr,
-				pasn->cipher, dot11RSNAConfigPMKLifetime,
-				&pasn->ptk,
+				pasn_get_cipher(pasn),
+				dot11RSNAConfigPMKLifetime,
+				pasn_get_ptk(pasn),
 				wpa_s->pasn_params ? wpas_pasn_deauth_cb : NULL,
-				wpa_s->pasn_params ? wpa_s : NULL, pasn->akmp);
+				wpa_s->pasn_params ? wpa_s : NULL,
+				pasn_get_akmp(pasn));
 
 		if (pasn->pmksa_entry)
 			wpa_sm_set_cur_pmksa(wpa_s->wpa, pasn->pmksa_entry);
 	}
 
-	forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
+	forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
 
 	if (ret == -1) {
 		wpas_pasn_auth_stop(wpa_s);
@@ -909,7 +911,8 @@
 	}
 
 	wpas_pasn_set_keys_from_cache(wpa_s, pasn->own_addr, pasn->peer_addr,
-				      pasn->cipher, pasn->akmp);
+				      pasn_get_cipher(pasn),
+				      pasn_get_akmp(pasn));
 	wpas_pasn_auth_stop(wpa_s);
 	wpas_pasn_auth_work_done(wpa_s, PASN_STATUS_SUCCESS);
 
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
index 7ce854b..2ec4310 100644
--- a/wpa_supplicant/rrm.c
+++ b/wpa_supplicant/rrm.c
@@ -515,6 +515,8 @@
 		num_primary_channels = 4;
 	else if (op->bw == BW160)
 		num_primary_channels = 8;
+	else if (op->bw == BW320)
+		num_primary_channels = 16;
 	else
 		num_primary_channels = 1;
 
@@ -561,6 +563,7 @@
 	u8 channels_80mhz_6ghz[] = { 7, 23, 39, 55, 71, 87, 103, 119, 135, 151,
 				     167, 183, 199, 215 };
 	u8 channels_160mhz_6ghz[] = { 15, 47, 79, 111, 143, 175, 207 };
+	u8 channels_320mhz_6ghz[] = { 31, 63, 95, 127, 159, 191 };
 	const u8 *channels = NULL;
 	size_t num_chan = 0;
 	bool is_6ghz = is_6ghz_op_class(op->op_class);
@@ -579,6 +582,9 @@
 			channels_160mhz_5ghz;
 		num_chan =  is_6ghz ? ARRAY_SIZE(channels_160mhz_6ghz) :
 			ARRAY_SIZE(channels_160mhz_5ghz);
+	} else if (op->bw == BW320) {
+		channels = channels_320mhz_6ghz;
+		num_chan = ARRAY_SIZE(channels_320mhz_6ghz);
 	}
 
 	return wpas_add_channels(op, mode, channels, num_chan);
@@ -1520,10 +1526,7 @@
 	if (!wpa_s->valid_links)
 		return ether_addr_equal(wpa_s->current_bss->bssid, bssid);
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		if (ether_addr_equal(wpa_s->links[i].bssid, bssid))
 			return true;
 	}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 6e6f05d..ccd694b 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -698,10 +698,7 @@
 	else
 		wpa_printf(MSG_DEBUG, "MLD: Probing links 0x%04x", links);
 
-	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
-		if (!(links & BIT(link_id)))
-			continue;
-
+	for_each_link(links, link_id) {
 		wpabuf_put_u8(extra_ie, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
 
 		/* Subelement length includes only the control */
@@ -2606,8 +2603,8 @@
 }
 
 
-void filter_scan_res(struct wpa_supplicant *wpa_s,
-		     struct wpa_scan_results *res)
+static void filter_scan_res(struct wpa_supplicant *wpa_s,
+			    struct wpa_scan_results *res)
 {
 	size_t i, j;
 
@@ -3169,6 +3166,7 @@
  * @wpa_s: Pointer to wpa_supplicant data
  * @info: Information about what was scanned or %NULL if not available
  * @new_scan: Whether a new scan was performed
+ * @bssid: Return BSS entries only for a single BSSID, %NULL for all
  * Returns: Scan results, %NULL on failure
  *
  * This function request the current scan results from the driver and updates
@@ -3177,13 +3175,14 @@
  */
 struct wpa_scan_results *
 wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
-				struct scan_info *info, int new_scan)
+				struct scan_info *info, int new_scan,
+				const u8 *bssid)
 {
 	struct wpa_scan_results *scan_res;
 	size_t i;
 	int (*compar)(const void *, const void *) = wpa_scan_result_compar;
 
-	scan_res = wpa_drv_get_scan_results2(wpa_s);
+	scan_res = wpa_drv_get_scan_results(wpa_s, bssid);
 	if (scan_res == NULL) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
 		return NULL;
@@ -3241,6 +3240,7 @@
 /**
  * wpa_supplicant_update_scan_results - Update scan results from the driver
  * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: Update BSS entries only for a single BSSID, %NULL for all
  * Returns: 0 on success, -1 on failure
  *
  * This function updates the BSS table within wpa_supplicant based on the
@@ -3250,10 +3250,11 @@
  * needed information to complete the connection (e.g., to perform validation
  * steps in 4-way handshake).
  */
-int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s,
+				       const u8 *bssid)
 {
 	struct wpa_scan_results *scan_res;
-	scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+	scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0, bssid);
 	if (scan_res == NULL)
 		return -1;
 	wpa_scan_results_free(scan_res);
@@ -3350,6 +3351,7 @@
 	params->duration = src->duration;
 	params->duration_mandatory = src->duration_mandatory;
 	params->oce_scan = src->oce_scan;
+	params->link_id = src->link_id;
 
 	if (src->sched_scan_plans_num > 0) {
 		params->sched_scan_plans =
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 8402e74..d4c06c1 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -54,8 +54,10 @@
 				bool default_ies, bool next);
 struct wpa_scan_results *
 wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
-				struct scan_info *info, int new_scan);
-int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s);
+				struct scan_info *info, int new_scan,
+				const u8 *bssid);
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s,
+				       const u8 *bssid);
 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
 const u8 * wpa_scan_get_ml_ie(const struct wpa_scan_res *res, u8 type);
 const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
@@ -86,8 +88,6 @@
 int wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant *wpa_s,
 				     unsigned int type, u8 *mask);
 int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
-void filter_scan_res(struct wpa_supplicant *wpa_s,
-		     struct wpa_scan_results *res);
 void scan_snr(struct wpa_scan_res *res);
 void scan_est_throughput(struct wpa_supplicant *wpa_s,
 			 struct wpa_scan_res *res);
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index b8f7c65..be0bc0d 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -182,7 +182,7 @@
 	if (!bss) {
 		wpa_printf(MSG_DEBUG,
 			   "SAE: BSS not available, update scan result to get BSS");
-		wpa_supplicant_update_scan_results(wpa_s);
+		wpa_supplicant_update_scan_results(wpa_s, bssid);
 		bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
 	}
 	if (bss) {
@@ -405,10 +405,7 @@
 		return bss;
 
 	if (!is_zero_ether_addr(wpa_s->conf->mld_connect_bssid_pref)) {
-		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-			if (!(wpa_s->valid_links & BIT(i)))
-				continue;
-
+		for_each_link(wpa_s->valid_links, i) {
 			if (wpa_s->mlo_assoc_link_id == i)
 				continue;
 
@@ -439,10 +436,7 @@
 		return bss;
 	}
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		if (wpa_s->mlo_assoc_link_id == i)
 			continue;
 
@@ -523,21 +517,23 @@
 static void wpas_sme_set_mlo_links(struct wpa_supplicant *wpa_s,
 				   struct wpa_bss *bss)
 {
-	int i;
+	u8 i;
 
 	wpa_s->valid_links = 0;
+	wpa_s->mlo_assoc_link_id = bss->mld_link_id;
 
-	for (i = 0; i < bss->n_mld_links; i++) {
-		u8 link_id = bss->mld_links[i].link_id;
+	for_each_link(bss->valid_links, i) {
 		const u8 *bssid = bss->mld_links[i].bssid;
 
-		if (i == 0)
-			wpa_s->mlo_assoc_link_id = link_id;
-		wpa_s->valid_links |= BIT(link_id);
-		os_memcpy(wpa_s->links[link_id].bssid, bssid, ETH_ALEN);
-		wpa_s->links[link_id].freq = bss->mld_links[i].freq;
-		wpa_s->links[link_id].bss = wpa_bss_get_bssid(wpa_s, bssid);
-		wpa_s->links[link_id].disabled = bss->mld_links[i].disabled;
+		wpa_s->valid_links |= BIT(i);
+		os_memcpy(wpa_s->links[i].bssid, bssid, ETH_ALEN);
+		wpa_s->links[i].freq = bss->mld_links[i].freq;
+		wpa_s->links[i].disabled = bss->mld_links[i].disabled;
+
+		if (bss->mld_link_id == i)
+			wpa_s->links[i].bss = bss;
+		else
+			wpa_s->links[i].bss = wpa_bss_get_bssid(wpa_s, bssid);
 	}
 }
 
@@ -578,7 +574,7 @@
 	if ((wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) &&
 	    !wpa_bss_parse_basic_ml_element(wpa_s, bss, wpa_s->ap_mld_addr,
 					    NULL, ssid, NULL) &&
-	    bss->n_mld_links) {
+	    bss->valid_links) {
 		wpa_printf(MSG_DEBUG, "MLD: In authentication");
 		wpas_sme_set_mlo_links(wpa_s, bss);
 
@@ -2416,12 +2412,16 @@
 
 	if (ssid && ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
+		struct multi_ap_params multi_ap = { 0 };
+
+		multi_ap.capability = MULTI_AP_BACKHAUL_STA;
+		multi_ap.profile = ssid->multi_ap_profile;
 
 		multi_ap_ie_len = add_multi_ap_ie(
 			wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
 			sizeof(wpa_s->sme.assoc_req_ie) -
 			wpa_s->sme.assoc_req_ie_len,
-			MULTI_AP_BACKHAUL_STA);
+			&multi_ap);
 		if (multi_ap_ie_len == 0) {
 			wpa_printf(MSG_ERROR,
 				   "Multi-AP: Failed to build Multi-AP IE");
@@ -2601,10 +2601,7 @@
 		params.mld_params.mld_addr = wpa_s->ap_mld_addr;
 		params.mld_params.valid_links = wpa_s->valid_links;
 		params.mld_params.assoc_link_id = wpa_s->mlo_assoc_link_id;
-		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-			if (!(wpa_s->valid_links & BIT(i)))
-				continue;
-
+		for_each_link(wpa_s->valid_links, i) {
 			params.mld_params.mld_links[i].bssid =
 				wpa_s->links[i].bssid;
 			params.mld_params.mld_links[i].freq =
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 775e75b..ea79ae6 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -418,7 +418,7 @@
 }
 
 
-void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
+void wnm_btm_reset(struct wpa_supplicant *wpa_s)
 {
 	int i;
 
@@ -431,8 +431,17 @@
 	os_free(wpa_s->wnm_neighbor_report_elements);
 	wpa_s->wnm_neighbor_report_elements = NULL;
 
-	wpabuf_free(wpa_s->coloc_intf_elems);
-	wpa_s->coloc_intf_elems = NULL;
+	wpa_s->wnm_cand_valid_until.sec = 0;
+	wpa_s->wnm_cand_valid_until.usec = 0;
+
+	wpa_s->wnm_mode = 0;
+	wpa_s->wnm_dialog_token = 0;
+	wpa_s->wnm_reply = 0;
+
+#ifdef CONFIG_MBO
+	wpa_s->wnm_mbo_trans_reason_present = 0;
+	wpa_s->wnm_mbo_transition_reason = 0;
+#endif /* CONFIG_MBO */
 }
 
 
@@ -781,22 +790,11 @@
 			}
 		}
 
-		if (bss->ssid_len != target->ssid_len ||
-		    os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
-			/*
-			 * TODO: Could consider allowing transition to another
-			 * ESS if PMF was enabled for the association.
-			 */
-			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
-				   " (pref %d) in different ESS",
-				   MAC2STR(nei->bssid),
-				   nei->preference_present ? nei->preference :
-				   -1);
-			continue;
-		}
-
-		if (wpa_s->current_ssid &&
-		    !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
+		/*
+		 * TODO: Could consider allowing transition to another ESS if
+		 * PMF was enabled for the association.
+		 */
+		if (!wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
 					1, 0)) {
 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
 				   " (pref %d) does not match the current network profile",
@@ -806,14 +804,6 @@
 			continue;
 		}
 
-		if (wpa_is_bss_tmp_disallowed(wpa_s, target)) {
-			wpa_printf(MSG_DEBUG,
-				   "MBO: Candidate BSS " MACSTR
-				   " retry delay is not over yet",
-				   MAC2STR(nei->bssid));
-			continue;
-		}
-
 		if (target->level < bss->level && target->level < -80) {
 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
 				   " (pref %d) does not have sufficient signal level (%d)",
@@ -1045,8 +1035,8 @@
 
 #define BTM_RESP_MIN_SIZE	5 + ETH_ALEN
 
-static void wnm_send_bss_transition_mgmt_resp(
-	struct wpa_supplicant *wpa_s, u8 dialog_token,
+static int wnm_send_bss_transition_mgmt_resp(
+	struct wpa_supplicant *wpa_s,
 	enum bss_trans_mgmt_status_code status,
 	enum mbo_transition_reject_reason reason,
 	u8 delay, const u8 *target_bssid)
@@ -1054,21 +1044,24 @@
 	struct wpabuf *buf;
 	int res;
 
+	wpa_s->wnm_reply = 0;
+
 	wpa_printf(MSG_DEBUG,
 		   "WNM: Send BSS Transition Management Response to " MACSTR
 		   " dialog_token=%u status=%u reason=%u delay=%d",
-		   MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay);
+		   MAC2STR(wpa_s->bssid), wpa_s->wnm_dialog_token, status,
+		   reason, delay);
 	if (!wpa_s->current_bss) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Current BSS not known - drop response");
-		return;
+		return -1;
 	}
 
 	buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Failed to allocate memory for BTM response");
-		return;
+		return -1;
 	}
 
 	wpa_s->bss_tm_status = status;
@@ -1076,7 +1069,7 @@
 
 	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
 	wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
-	wpabuf_put_u8(buf, dialog_token);
+	wpabuf_put_u8(buf, wpa_s->wnm_dialog_token);
 	wpabuf_put_u8(buf, status);
 	wpabuf_put_u8(buf, delay);
 	if (target_bssid) {
@@ -1090,7 +1083,7 @@
 		wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
 	}
 
-	if (status == WNM_BSS_TM_ACCEPT && !wpa_s->wnm_link_removal)
+	if (status == WNM_BSS_TM_ACCEPT && target_bssid)
 		wnm_add_cand_list(wpa_s, &buf);
 
 #ifdef CONFIG_MBO
@@ -1106,7 +1099,7 @@
 				wpabuf_free(buf);
 				wpa_printf(MSG_DEBUG,
 					   "WNM: Failed to allocate memory for MBO IE");
-				return;
+				return -1;
 			}
 
 			wpabuf_put_data(buf, mbo, ret);
@@ -1123,6 +1116,8 @@
 	}
 
 	wpabuf_free(buf);
+
+	return res;
 }
 
 
@@ -1140,19 +1135,24 @@
 
 	/* Send the BSS Management Response - Accept */
 	if (wpa_s->wnm_reply) {
-		wpa_s->wnm_reply = 0;
+		wpa_s->wnm_target_bss = bss;
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Sending successful BSS Transition Management Response");
-		wnm_send_bss_transition_mgmt_resp(
-			wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
-			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
-			bss->bssid);
+
+		/* This function will be called again from the TX handler to
+		 * start the actual reassociation after this response has been
+		 * delivered to the current AP. */
+		if (wnm_send_bss_transition_mgmt_resp(
+			    wpa_s, WNM_BSS_TM_ACCEPT,
+			    MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
+			    bss->bssid) >= 0)
+			return;
 	}
 
 	if (bss == wpa_s->current_bss) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Already associated with the preferred candidate");
-		wnm_deallocate_memory(wpa_s);
+		wnm_btm_reset(wpa_s);
 		return;
 	}
 
@@ -1168,11 +1168,10 @@
 	 */
 	if (!already_connecting && radio_work_pending(wpa_s, "sme-connect"))
 		wpa_s->bss_trans_mgmt_in_progress = true;
-	wnm_deallocate_memory(wpa_s);
 }
 
 
-int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
+int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check)
 {
 	struct wpa_bss *bss;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
@@ -1180,27 +1179,51 @@
 	enum mbo_transition_reject_reason reason =
 		MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
 
-	if (!wpa_s->wnm_neighbor_report_elements)
+	if (!wpa_s->wnm_dialog_token)
 		return 0;
 
 	wpa_dbg(wpa_s, MSG_DEBUG,
 		"WNM: Process scan results for BSS Transition Management");
-	if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
+	if (!pre_scan_check &&
+	    os_reltime_initialized(&wpa_s->wnm_cand_valid_until) &&
+	    os_reltime_before(&wpa_s->wnm_cand_valid_until,
 			      &wpa_s->scan_trigger_time)) {
 		wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
-		wnm_deallocate_memory(wpa_s);
-		return 0;
-	}
-
-	if (!wpa_s->current_bss ||
-	    !ether_addr_equal(wpa_s->wnm_cand_from_bss,
-			      wpa_s->current_bss->bssid)) {
-		wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
-		return 0;
+		goto send_bss_resp_fail;
 	}
 
 	/* Compare the Neighbor Report and scan results */
 	bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
+
+	/*
+	 * If this is a pre-scan check, returning 0 will trigger a scan and
+	 * another call. In that case, reject "bad" candidates in the hope of
+	 * finding a better candidate after scanning.
+	 *
+	 * Use a simple heuristic to check whether the selection is reasonable
+	 * or a scan is a good idea. For that, we need to have found a
+	 * candidate BSS (which might be the current one), it is up-to-date,
+	 * and we don't want to immediately roam back again.
+	 */
+	if (pre_scan_check) {
+		struct os_reltime age;
+
+		if (!bss)
+			return 0;
+
+		os_reltime_age(&bss->last_update, &age);
+		if (age.sec >= 10)
+			return 0;
+
+#ifndef CONFIG_NO_ROAMING
+		if (wpa_s->current_bss && bss != wpa_s->current_bss &&
+		    wpa_supplicant_need_to_roam_within_ess(wpa_s,
+							   wpa_s->current_bss,
+							   bss))
+			return 0;
+#endif /* CONFIG_NO_ROAMING */
+	}
+
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
 		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
@@ -1212,18 +1235,13 @@
 	return 1;
 
 send_bss_resp_fail:
-	if (!reply_on_fail)
-		return 0;
-
 	/* Send reject response for all the failures */
 
-	if (wpa_s->wnm_reply) {
-		wpa_s->wnm_reply = 0;
-		wnm_send_bss_transition_mgmt_resp(wpa_s,
-						  wpa_s->wnm_dialog_token,
-						  status, reason, 0, NULL);
-	}
-	wnm_deallocate_memory(wpa_s);
+	if (wpa_s->wnm_reply)
+		wnm_send_bss_transition_mgmt_resp(wpa_s, status, reason,
+						  0, NULL);
+
+	wnm_btm_reset(wpa_s);
 
 	return 0;
 }
@@ -1330,6 +1348,10 @@
 		struct neighbor_report *nei;
 
 		nei = &wpa_s->wnm_neighbor_report_elements[i];
+
+		if (nei->preference_present && nei->preference == 0)
+			continue;
+
 		if (nei->freq <= 0) {
 			wpa_printf(MSG_DEBUG,
 				   "WNM: Unknown neighbor operating frequency for "
@@ -1354,79 +1376,6 @@
 }
 
 
-static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
-{
-	struct wpa_scan_results *scan_res;
-	struct wpa_bss *bss;
-	struct wpa_ssid *ssid = wpa_s->current_ssid;
-	u8 i, found = 0;
-	size_t j;
-
-	wpa_dbg(wpa_s, MSG_DEBUG,
-		"WNM: Fetch current scan results from the driver for checking transition candidates");
-	scan_res = wpa_drv_get_scan_results2(wpa_s);
-	if (!scan_res) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results");
-		return 0;
-	}
-
-	if (scan_res->fetch_time.sec == 0)
-		os_get_reltime(&scan_res->fetch_time);
-
-	filter_scan_res(wpa_s, scan_res);
-
-	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
-		struct neighbor_report *nei;
-
-		nei = &wpa_s->wnm_neighbor_report_elements[i];
-		if (nei->preference_present && nei->preference == 0)
-			continue;
-
-		for (j = 0; j < scan_res->num; j++) {
-			struct wpa_scan_res *res;
-			const u8 *ssid_ie;
-
-			res = scan_res->res[j];
-			if (!ether_addr_equal(nei->bssid, res->bssid) ||
-			    res->age > WNM_SCAN_RESULT_AGE * 1000)
-				continue;
-			bss = wpa_s->current_bss;
-			ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
-			if (bss && ssid_ie && ssid_ie[1] &&
-			    (bss->ssid_len != ssid_ie[1] ||
-			     os_memcmp(bss->ssid, ssid_ie + 2,
-				       bss->ssid_len) != 0))
-				continue; /* Skip entries for other ESSs */
-
-			/* Potential candidate found */
-			found = 1;
-			scan_snr(res);
-			scan_est_throughput(wpa_s, res);
-			wpa_bss_update_scan_res(wpa_s, res,
-						&scan_res->fetch_time);
-		}
-	}
-
-	wpa_scan_results_free(scan_res);
-	if (!found) {
-		wpa_dbg(wpa_s, MSG_DEBUG,
-			"WNM: No transition candidate matches existing scan results");
-		return 0;
-	}
-
-	bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL);
-	if (!bss) {
-		wpa_dbg(wpa_s, MSG_DEBUG,
-			"WNM: Comparison of scan results against transition candidates did not find matches");
-		return 0;
-	}
-
-	/* Associate to the network */
-	wnm_bss_tm_connect(wpa_s, bss, ssid, 0);
-	return 1;
-}
-
-
 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
 					     const u8 *pos, const u8 *end,
 					     int reply)
@@ -1445,8 +1394,6 @@
 		return;
 
 #ifdef CONFIG_MBO
-	wpa_s->wnm_mbo_trans_reason_present = 0;
-	wpa_s->wnm_mbo_transition_reason = 0;
 	wpa_s->wnm_mbo_cell_pref_present = 0;
 	wpa_s->wnm_mbo_cell_preference = 0;
 	wpa_s->wnm_mbo_assoc_retry_delay_present = 0;
@@ -1458,6 +1405,8 @@
 	else
 		beacon_int = 100; /* best guess */
 
+	wnm_btm_reset(wpa_s);
+
 	wpa_s->wnm_dialog_token = pos[0];
 	wpa_s->wnm_mode = pos[1];
 	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
@@ -1477,8 +1426,7 @@
 			   "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,
+			wpa_s, wpa_s->reject_btm_req_reason,
 			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
 		return;
 	}
@@ -1541,15 +1489,34 @@
 	 * set to 1, and the BSS Termination Included field is set to 1, only
 	 * one of the links is removed and the other links remain associated.
 	 * Ignore the Disassociation Imminent field in such a case.
+	 *
+	 * TODO: We should check if the AP has more than one link.
+	 * TODO: We should pass the RX link and use that
 	 */
-	if (disassoc_imminent &&
-	    (wpa_s->valid_links & (wpa_s->valid_links - 1)) != 0 &&
+	if (disassoc_imminent && wpa_s->valid_links &&
 	    (wpa_s->wnm_mode & WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT) &&
 	    (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED)) {
-		wpa_printf(MSG_INFO,
-			   "WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
-		disassoc_imminent = false;
+		/* If we still have a link, then just accept the request */
+		if (wpa_s->valid_links & (wpa_s->valid_links - 1)) {
+			wpa_printf(MSG_INFO,
+				   "WNM: BTM request for a single MLO link - ignore disassociation imminent since other links remain associated");
+			disassoc_imminent = false;
+
+			wnm_send_bss_transition_mgmt_resp(
+				wpa_s, WNM_BSS_TM_ACCEPT, 0, 0, NULL);
+
+			return;
+		}
+
+		/* The last link is being removed (which must be the assoc link)
+		 */
 		wpa_s->wnm_link_removal = true;
+		os_memcpy(wpa_s->wnm_dissoc_addr,
+			  wpa_s->links[wpa_s->mlo_assoc_link_id].bssid,
+			  ETH_ALEN);
+	} else {
+		os_memcpy(wpa_s->wnm_dissoc_addr, wpa_s->valid_links ?
+			  wpa_s->ap_mld_addr : wpa_s->bssid, ETH_ALEN);
 	}
 
 	if (disassoc_imminent) {
@@ -1566,7 +1533,6 @@
 		unsigned int valid_ms;
 
 		wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
-		wnm_deallocate_memory(wpa_s);
 		wpa_s->wnm_neighbor_report_elements = os_calloc(
 			WNM_MAX_NEIGHBOR_REPORT,
 			sizeof(struct neighbor_report));
@@ -1614,8 +1580,7 @@
 			wpa_printf(MSG_DEBUG,
 				   "WNM: Candidate list included bit is set, but no candidates found");
 			wnm_send_bss_transition_mgmt_resp(
-				wpa_s, wpa_s->wnm_dialog_token,
-				WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
+				wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
 				MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
 				NULL);
 			return;
@@ -1625,8 +1590,7 @@
 			wpa_printf(MSG_DEBUG,
 				   "WNM: Configuration prevents roaming (BSSID set)");
 			wnm_send_bss_transition_mgmt_resp(
-				wpa_s, wpa_s->wnm_dialog_token,
-				WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
+				wpa_s, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
 				MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
 				NULL);
 			return;
@@ -1643,35 +1607,21 @@
 		wpa_s->wnm_cand_valid_until.sec +=
 			wpa_s->wnm_cand_valid_until.usec / 1000000;
 		wpa_s->wnm_cand_valid_until.usec %= 1000000;
-		os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
 
 		/*
-		 * Fetch the latest scan results from the kernel and check for
-		 * candidates based on those results first. This can help in
-		 * finding more up-to-date information should the driver has
-		 * done some internal scanning operations after the last scan
-		 * result update in wpa_supplicant.
-		 */
-		if (wnm_fetch_scan_results(wpa_s) > 0)
+		* Try fetching the latest scan results from the kernel.
+		* This can help in finding more up-to-date information should
+		* the driver have done some internal scanning operations after
+		* the last scan result update in wpa_supplicant.
+		*
+		* It is not a new scan, this does not update the last_scan
+		* timestamp nor will it expire old BSSs.
+		*/
+		wpa_supplicant_update_scan_results(wpa_s, NULL);
+		if (wnm_scan_process(wpa_s, true) > 0)
 			return;
-
-		/*
-		 * Try to use previously received scan results, if they are
-		 * recent enough to use for a connection.
-		 */
-		if (wpa_s->last_scan_res_used > 0) {
-			struct os_reltime now;
-
-			os_get_reltime(&now);
-			if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
-				wpa_printf(MSG_DEBUG,
-					   "WNM: Try to use recent scan results");
-				if (wnm_scan_process(wpa_s, 0) > 0)
-					return;
-				wpa_printf(MSG_DEBUG,
-					   "WNM: No match in previous scan results - try a new scan");
-			}
-		}
+		wpa_printf(MSG_DEBUG,
+			   "WNM: No valid match in previous scan results - try a new scan");
 
 		wnm_set_scan_freqs(wpa_s);
 		if (wpa_s->wnm_num_neighbor_report == 1) {
@@ -1694,12 +1644,45 @@
 			status = WNM_BSS_TM_REJECT_UNSPECIFIED;
 		}
 		wnm_send_bss_transition_mgmt_resp(
-			wpa_s, wpa_s->wnm_dialog_token, status,
+			wpa_s, status,
 			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
 	}
 }
 
 
+int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+			   size_t data_len)
+{
+	const struct ieee80211_mgmt *frame =
+		(const struct ieee80211_mgmt *) data;
+
+	if (data_len <
+	    IEEE80211_HDRLEN + sizeof(frame->u.action.u.bss_tm_resp) ||
+	    frame->u.action.category != WLAN_ACTION_WNM ||
+	    frame->u.action.u.bss_tm_resp.action != WNM_BSS_TRANS_MGMT_RESP ||
+	    frame->u.action.u.bss_tm_resp.status_code != WNM_BSS_TM_ACCEPT)
+		return -1;
+
+	/*
+	 * If disassoc imminent bit was set in the request, the response may
+	 * indicate accept even if no candidate was found, so bail out here.
+	 */
+	if (!wpa_s->wnm_target_bss) {
+		wpa_printf(MSG_DEBUG, "WNM: Target BSS is not set");
+		return 0;
+	}
+
+	if (!wpa_s->current_ssid)
+		return 0;
+
+	wnm_bss_tm_connect(wpa_s, wpa_s->wnm_target_bss, wpa_s->current_ssid,
+			   0);
+
+	wpa_s->wnm_target_bss = NULL;
+	return 0;
+}
+
+
 #define BTM_QUERY_MIN_SIZE	4
 
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
@@ -2052,14 +2035,14 @@
 void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
 			      struct wpabuf *elems)
 {
-	wpabuf_free(wpa_s->coloc_intf_elems);
 	if (elems && wpabuf_len(elems) == 0) {
 		wpabuf_free(elems);
 		elems = NULL;
 	}
-	wpa_s->coloc_intf_elems = elems;
 
-	if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems &&
+	/* NOTE: The elements are not stored as they are only send out once */
+
+	if (wpa_s->conf->coloc_intf_reporting && elems &&
 	    wpa_s->coloc_intf_dialog_token &&
 	    (wpa_s->coloc_intf_auto_report == 1 ||
 	     wpa_s->coloc_intf_auto_report == 3)) {
@@ -2068,8 +2051,10 @@
 		 */
 		wnm_send_coloc_intf_report(wpa_s,
 					   wpa_s->coloc_intf_dialog_token,
-					   wpa_s->coloc_intf_elems);
+					   elems);
 	}
+
+	wpabuf_free(elems);
 }
 
 
@@ -2082,8 +2067,6 @@
 
 bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
-	unsigned int i;
-
 	if (!(wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))
 		return false;
 
@@ -2091,26 +2074,14 @@
 	 * In case disassociation imminent is set, do no try to use a BSS to
 	 * which we are connected.
 	 */
-
-	if (wpa_s->current_bss &&
-	    ether_addr_equal(wpa_s->current_bss->bssid, bss->bssid)) {
-		wpa_dbg(wpa_s, MSG_DEBUG,
-			"WNM: Disassociation imminent: current BSS");
-		return true;
-	}
-
-	if (!wpa_s->valid_links)
-		return false;
-
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
-		if (ether_addr_equal(wpa_s->links[i].bssid, bss->bssid)) {
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"WNM: MLD: Disassociation imminent: current link");
+	if (wpa_s->wnm_link_removal ||
+	    !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_MLO) ||
+	    is_zero_ether_addr(bss->mld_addr)) {
+		if (ether_addr_equal(bss->bssid, wpa_s->wnm_dissoc_addr))
 			return true;
-		}
+	} else {
+		if (ether_addr_equal(bss->mld_addr, wpa_s->wnm_dissoc_addr))
+			return true;
 	}
 
 	return false;
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 2a473db..235a838 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -65,19 +65,28 @@
 				       const char *btm_candidates,
 				       int cand_list);
 
-void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
 int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
 			       const struct wpabuf *elems);
 void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
 			      struct wpabuf *elems);
-bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
 
+int wnm_btm_resp_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+			   size_t data_len);
 
 #ifdef CONFIG_WNM
 
-int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
+int wnm_scan_process(struct wpa_supplicant *wpa_s, bool pre_scan_check);
 void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s);
 
+bool wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+
+void wnm_btm_reset(struct wpa_supplicant *wpa_s);
+
+static inline bool wnm_active_bss_trans_mgmt(struct wpa_supplicant *wpa_s)
+{
+	return !!wpa_s->wnm_dialog_token;
+}
+
 #else /* CONFIG_WNM */
 
 static inline int wnm_scan_process(struct wpa_supplicant *wpa_s,
@@ -90,6 +99,21 @@
 {
 }
 
+static inline bool
+wnm_is_bss_excluded(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	return false;
+}
+
+static inline void wnm_btm_reset(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline bool wnm_active_bss_trans_mgmt(struct wpa_supplicant *wpa_s)
+{
+	return false;
+}
+
 #endif /* CONFIG_WNM */
 
 #endif /* WNM_STA_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index b1334e2..60f8562 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -5129,7 +5129,7 @@
 
 	eloop_register_signal_terminate(wpa_cli_terminate, NULL);
 
-	if (ctrl_ifname == NULL)
+	if (!ctrl_ifname && !global)
 		ctrl_ifname = wpa_cli_get_default_ifname();
 
 	if (reconnect && action_file && ctrl_ifname) {
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 31a9af6..88f3f2a 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -187,7 +187,10 @@
 	int val;
 	size_t i;
 
-	res = iface->driver->get_scan_results2(iface->drv_priv);
+	if (iface->driver->get_scan_results)
+		res = iface->driver->get_scan_results(iface->drv_priv, NULL);
+	else
+		res = iface->driver->get_scan_results2(iface->drv_priv);
 	if (res == NULL)
 		goto fail;
 
@@ -231,7 +234,7 @@
 	if (iface->drv_priv == NULL)
 		return;
 
-	if (iface->driver->get_scan_results2)
+	if (iface->driver->get_scan_results || iface->driver->get_scan_results2)
 		wpa_priv_get_scan_results2(iface, from, fromlen);
 	else
 		sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index a851024..3fde0a8 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -678,9 +678,7 @@
 	wpa_s->disallow_aps_ssid = NULL;
 
 	wnm_bss_keep_alive_deinit(wpa_s);
-#ifdef CONFIG_WNM
-	wnm_deallocate_memory(wpa_s);
-#endif /* CONFIG_WNM */
+	wnm_btm_reset(wpa_s);
 
 	ext_password_deinit(wpa_s->ext_pw);
 	wpa_s->ext_pw = NULL;
@@ -894,7 +892,7 @@
 			struct wpa_scan_results *scan_res;
 			wpa_s->bgscan_ssid = wpa_s->current_ssid;
 			scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL,
-								   0);
+								   0, NULL);
 			if (scan_res) {
 				bgscan_notify_scan(wpa_s, scan_res);
 				wpa_scan_results_free(scan_res);
@@ -1081,6 +1079,10 @@
 	if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
 		wpa_supplicant_start_autoscan(wpa_s);
 
+	if (state == WPA_COMPLETED || state == WPA_INTERFACE_DISABLED ||
+	    state == WPA_INACTIVE)
+		wnm_btm_reset(wpa_s);
+
 #ifndef CONFIG_NO_WMM_AC
 	if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED)
 		wmm_ac_notify_disassoc(wpa_s);
@@ -2522,6 +2524,7 @@
 #endif /* CONFIG_NO_WMM_AC */
 #ifdef CONFIG_WNM
 	wpa_s->wnm_mode = 0;
+	wpa_s->wnm_target_bss = NULL;
 #endif /* CONFIG_WNM */
 	wpa_s->reassoc_same_bss = 0;
 	wpa_s->reassoc_same_ess = 0;
@@ -2919,7 +2922,8 @@
 	if (obss_scan) {
 		struct wpa_scan_results *scan_res;
 
-		scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+		scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0,
+							   NULL);
 		if (scan_res == NULL) {
 			/* Back to HT20 */
 			freq->sec_channel_offset = 0;
@@ -3083,7 +3087,7 @@
 				    freq->sec_channel_offset,
 				    chwidth, seg0, seg1, vht_caps,
 				    &mode->he_capab[ieee80211_mode],
-				    &mode->eht_capab[ieee80211_mode]) != 0)
+				    &mode->eht_capab[ieee80211_mode], 0) != 0)
 		return false;
 
 	*freq = vht_freq;
@@ -3831,10 +3835,14 @@
 
 	if (ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
+		struct multi_ap_params multi_ap = { 0 };
+
+		multi_ap.capability = MULTI_AP_BACKHAUL_STA;
+		multi_ap.profile = ssid->multi_ap_profile;
 
 		multi_ap_ie_len = add_multi_ap_ie(wpa_ie + wpa_ie_len,
 						  max_wpa_ie_len - wpa_ie_len,
-						  MULTI_AP_BACKHAUL_STA);
+						  &multi_ap);
 		if (multi_ap_ie_len == 0) {
 			wpa_printf(MSG_ERROR,
 				   "Multi-AP: Failed to build Multi-AP IE");
@@ -9173,8 +9181,7 @@
 		if (modes[i].mode != mode ||
 		    !modes[i].num_channels || !modes[i].channels)
 			continue;
-		if ((!is_6ghz && !is_6ghz_freq(modes[i].channels[0].freq)) ||
-		    (is_6ghz && is_6ghz_freq(modes[i].channels[0].freq)))
+		if (is_6ghz == modes[i].is_6ghz)
 			return &modes[i];
 	}
 
@@ -9413,17 +9420,21 @@
 
 
 struct wpa_scan_results *
-wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s)
+wpa_drv_get_scan_results(struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
 	struct wpa_scan_results *scan_res;
 #ifdef CONFIG_TESTING_OPTIONS
 	size_t idx;
 #endif /* CONFIG_TESTING_OPTIONS */
 
-	if (!wpa_s->driver->get_scan_results2)
+	if (wpa_s->driver->get_scan_results)
+		scan_res = wpa_s->driver->get_scan_results(wpa_s->drv_priv,
+							   bssid);
+	else if (wpa_s->driver->get_scan_results2)
+		scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
+	else
 		return NULL;
 
-	scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
 
 #ifdef CONFIG_TESTING_OPTIONS
 	for (idx = 0; scan_res && idx < scan_res->num; idx++) {
@@ -9461,10 +9472,7 @@
 	if (!wpa_s->valid_links)
 		return false;
 
-	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
-		if (!(wpa_s->valid_links & BIT(i)))
-			continue;
-
+	for_each_link(wpa_s->valid_links, i) {
 		if (ether_addr_equal(wpa_s->links[i].bssid, addr))
 			return true;
 	}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index d689441..89c5d03 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1717,6 +1717,12 @@
 # support Multi-AP, and sets 4-address mode if it does. Thus, the netdev can be
 # added to a bridge to allow forwarding frames over this backhaul link.
 
+# Multi-AP Profile
+# Indicate the supported Multi-AP profile
+# 1 = Supports Multi-AP profile 1 as defined in Wi-Fi EasyMesh specification
+# 2 = Supports Multi-AP profile 2 as defined in Wi-Fi EasyMesh specification
+#multi_ap_profile=2
+
 ##### Fast Session Transfer (FST) support #####################################
 #
 # The options in this section are only available when the build configuration
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 3a5f61c..0fea5cc 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1327,14 +1327,14 @@
 	u8 wnm_num_neighbor_report;
 	u8 wnm_mode;
 	bool wnm_link_removal;
+	u8 wnm_dissoc_addr[ETH_ALEN];
 	u16 wnm_dissoc_timer;
 	u8 wnm_bss_termination_duration[12];
 	struct neighbor_report *wnm_neighbor_report_elements;
 	struct os_reltime wnm_cand_valid_until;
-	u8 wnm_cand_from_bss[ETH_ALEN];
+	struct wpa_bss *wnm_target_bss;
 	enum bss_trans_mgmt_status_code bss_tm_status;
 	bool bss_trans_mgmt_in_progress;
-	struct wpabuf *coloc_intf_elems;
 	u8 coloc_intf_dialog_token;
 	u8 coloc_intf_auto_report;
 	u8 coloc_intf_timeout;
@@ -1500,6 +1500,9 @@
 	int dpp_netrole;
 	int dpp_auth_ok_on_ack;
 	int dpp_in_response_listen;
+	bool dpp_tx_auth_resp_on_roc_stop;
+	bool dpp_tx_chan_change;
+	bool dpp_listen_on_tx_expire;
 	int dpp_gas_client;
 	int dpp_gas_server;
 	int dpp_gas_dialog_token;
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 111680c..2fea640 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -465,7 +465,7 @@
 
 	/* No WPA/RSN IE found in the cached scan results. Try to get updated
 	 * scan results from the driver. */
-	if (wpa_supplicant_update_scan_results(wpa_s) < 0)
+	if (wpa_supplicant_update_scan_results(wpa_s, wpa_s->bssid) < 0)
 		return -1;
 
 	return wpa_get_beacon_ie(wpa_s);
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 81e11e7..f103237 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1208,14 +1208,19 @@
 		}
 	}
 #endif /* CONFIG_P2P */
-	os_snprintf(phase1, sizeof(phase1), "pbc=1%s",
-		    multi_ap_backhaul_sta ? " multi_ap=1" : "");
+	if (multi_ap_backhaul_sta)
+		os_snprintf(phase1, sizeof(phase1), "pbc=1 multi_ap=%d",
+			    multi_ap_backhaul_sta);
+	else
+		os_snprintf(phase1, sizeof(phase1), "pbc=1");
 	if (wpa_config_set_quoted(ssid, "phase1", phase1) < 0)
 		return -1;
 	if (wpa_s->wps_fragment_size)
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
-	if (multi_ap_backhaul_sta)
+	if (multi_ap_backhaul_sta) {
 		ssid->multi_ap_backhaul_sta = 1;
+		ssid->multi_ap_profile = multi_ap_backhaul_sta;
+	}
 	wpa_s->supp_pbc_active = true;
 	wpa_s->wps_overlap = false;
 	wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);