Merge "wifi: Create 1.3::IfaceParams to support dual bands (AP+AP Part 1)"
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 58b0d16..3590b09 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -261,6 +261,10 @@
 ifdef CONFIG_SAE
 L_CFLAGS += -DCONFIG_SAE
 OBJS += src/common/sae.c
+ifdef CONFIG_SAE_PK
+L_CFLAGS += -DCONFIG_SAE_PK
+OBJS += src/common/sae_pk.c
+endif
 NEED_ECC=y
 NEED_DH_GROUPS=y
 NEED_HMAC_SHA256_KDF=y
@@ -425,7 +429,7 @@
 ifdef CONFIG_EAP_SIM_COMMON
 OBJS += src/eap_common/eap_sim_common.c
 # Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be
-# replaced with another file implementating the interface specified in
+# replaced with another file implementing the interface specified in
 # eap_sim_db.h.
 OBJS += src/eap_server/eap_sim_db.c
 NEED_FIPS186_2_PRF=y
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index 6c4410e..34a8a08 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -362,7 +362,7 @@
 	* RADIUS server functionality
 	  - add minimal RADIUS accounting server support (hostapd-as-server);
 	    this is mainly to enable testing coverage with hwsim scripts
-	  - allow authentication log to be written into SQLite databse
+	  - allow authentication log to be written into SQLite database
 	  - added option for TLS protocol testing of an EAP peer by simulating
 	    various misbehaviors/known attacks
 	  - MAC ACL support for testing purposes
@@ -668,7 +668,7 @@
 	* fixed HT Capabilities IE with nl80211 drivers
 	* moved generic AP functionality code into src/ap
 	* WPS: handle Selected Registrar as union of info from all Registrars
-	* remove obsolte Prism54.org driver wrapper
+	* remove obsolete Prism54.org driver wrapper
 	* added internal debugging mechanism with backtrace support and memory
 	  allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y)
 	* EAP-FAST server: piggyback Phase 2 start with the end of Phase 1
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 95ef085..066bfae 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -1,10 +1,7 @@
-ifndef CC
-CC=gcc
-endif
+ALL=hostapd hostapd_cli
+CONFIG_FILE = .config
 
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
+include ../src/build.rules
 
 ifdef LIBS
 # If LIBS is set with some global build system defaults, clone those for
@@ -19,6 +16,9 @@
 ifndef LIBS_n
 LIBS_n := $(LIBS)
 endif
+ifndef LIBS_s
+LIBS_s := $(LIBS)
+endif
 endif
 
 CFLAGS += $(EXTRA_CFLAGS)
@@ -27,8 +27,6 @@
 
 export BINDIR ?= /usr/local/bin/
 
--include .config
-
 ifndef CONFIG_NO_GITVER
 # Add VERSION_STR postfix for builds from a git repository
 ifeq ($(wildcard ../.git),../.git)
@@ -122,6 +120,7 @@
 LIBS_c += -lbfd -ldl -liberty -lz
 LIBS_h += -lbfd -ldl -liberty -lz
 LIBS_n += -lbfd -ldl -liberty -lz
+LIBS_s += -lbfd -ldl -liberty -lz
 endif
 endif
 
@@ -294,6 +293,10 @@
 ifdef CONFIG_SAE
 CFLAGS += -DCONFIG_SAE
 OBJS += ../src/common/sae.o
+ifdef CONFIG_SAE_PK
+CFLAGS += -DCONFIG_SAE_PK
+OBJS += ../src/common/sae_pk.o
+endif
 NEED_ECC=y
 NEED_DH_GROUPS=y
 NEED_HMAC_SHA256_KDF=y
@@ -445,7 +448,7 @@
 ifdef CONFIG_EAP_SIM_COMMON
 OBJS += ../src/eap_common/eap_sim_common.o
 # Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be
-# replaced with another file implementating the interface specified in
+# replaced with another file implementing the interface specified in
 # eap_sim_db.h.
 OBJS += ../src/eap_server/eap_sim_db.o
 NEED_FIPS186_2_PRF=y
@@ -704,6 +707,7 @@
 endif
 OBJS += ../src/crypto/crypto_openssl.o
 HOBJS += ../src/crypto/crypto_openssl.o
+SOBJS += ../src/crypto/crypto_openssl.o
 ifdef NEED_FIPS186_2_PRF
 OBJS += ../src/crypto/fips_prf_openssl.o
 endif
@@ -711,9 +715,11 @@
 LIBS += -lcrypto
 LIBS_h += -lcrypto
 LIBS_n += -lcrypto
+LIBS_s += -lcrypto
 ifdef CONFIG_TLS_ADD_DL
 LIBS += -ldl
 LIBS_h += -ldl
+LIBS_s += -ldl
 endif
 ifndef CONFIG_TLS_DEFAULT_CIPHERS
 CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
@@ -1257,60 +1263,25 @@
 CFLAGS += -DCONFIG_NO_TKIP
 endif
 
-ALL=hostapd hostapd_cli
-
-all: verify_config $(ALL)
-
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-ifeq ($(QUIET), 1)
-Q=@
-E=true
-endif
-
-ifdef CONFIG_CODE_COVERAGE
-%.o: %.c
-	@$(E) "  CC " $<
-	$(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<)
-else
-%.o: %.c
-	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
-	@$(E) "  CC " $<
-endif
-
-verify_config:
-	@if [ ! -r .config ]; then \
-		echo 'Building hostapd requires a configuration file'; \
-		echo '(.config). See README for more instructions. You can'; \
-		echo 'run "cp defconfig .config" to create an example'; \
-		echo 'configuration.'; \
-		exit 1; \
-	fi
-
 $(DESTDIR)$(BINDIR)/%: %
 	install -D $(<) $(@)
 
 install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
 
-../src/drivers/build.hostapd:
-	@if [ -f ../src/drivers/build.wpa_supplicant ]; then \
-		$(MAKE) -C ../src/drivers clean; \
-	fi
-	@touch ../src/drivers/build.hostapd
+_OBJS_VAR := OBJS
+include ../src/objs.mk
 
-BCHECK=../src/drivers/build.hostapd
-
-hostapd: $(BCHECK) $(OBJS)
+hostapd: $(OBJS)
 	$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
 	@$(E) "  LD " $@
 
 ifdef CONFIG_WPA_TRACE
 OBJS_c += ../src/utils/trace.o
 endif
+
+_OBJS_VAR := OBJS_c
+include ../src/objs.mk
+
 hostapd_cli: $(OBJS_c)
 	$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
 	@$(E) "  LD " $@
@@ -1345,6 +1316,35 @@
 HOBJS += ../src/crypto/crypto_linux.o
 endif
 
+SOBJS += sae_pk_gen.o
+SOBJS +=  ../src/utils/common.o
+SOBJS += ../src/utils/os_$(CONFIG_OS).o
+SOBJS += ../src/utils/base64.o
+SOBJS += ../src/utils/wpa_debug.o
+SOBJS += ../src/utils/wpabuf.o
+ifdef CONFIG_WPA_TRACE
+SOBJS += ../src/utils/trace.o
+endif
+SOBJS += ../src/common/ieee802_11_common.o
+SOBJS += ../src/common/sae.o
+SOBJS += ../src/common/sae_pk.o
+SOBJS += ../src/common/dragonfly.o
+SOBJS += $(AESOBJS)
+SOBJS += ../src/crypto/sha256-prf.o
+SOBJS += ../src/crypto/sha384-prf.o
+SOBJS += ../src/crypto/sha512-prf.o
+SOBJS += ../src/crypto/dh_groups.o
+SOBJS += ../src/crypto/sha256-kdf.o
+SOBJS += ../src/crypto/sha384-kdf.o
+SOBJS += ../src/crypto/sha512-kdf.o
+
+_OBJS_VAR := NOBJS
+include ../src/objs.mk
+_OBJS_VAR := HOBJS
+include ../src/objs.mk
+_OBJS_VAR := SOBJS
+include ../src/objs.mk
+
 nt_password_hash: $(NOBJS)
 	$(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
 	@$(E) "  LD " $@
@@ -1353,15 +1353,17 @@
 	$(Q)$(CC) $(LDFLAGS) -o hlr_auc_gw $(HOBJS) $(LIBS_h)
 	@$(E) "  LD " $@
 
+sae_pk_gen: $(SOBJS)
+	$(Q)$(CC) $(LDFLAGS) -o sae_pk_gen $(SOBJS) $(LIBS_s)
+	@$(E) "  LD " $@
+
+.PHONY: lcov-html
 lcov-html:
-	lcov -c -d .. > lcov.info
+	lcov -c -d $(BUILDDIR) > lcov.info
 	genhtml lcov.info --output-directory lcov-html
 
-clean:
-	$(MAKE) -C ../src clean
-	rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw
-	rm -f *.d *.gcno *.gcda *.gcov
+clean: common-clean
+	rm -f core *~ nt_password_hash hlr_auc_gw
+	rm -f sae_pk_gen
 	rm -f lcov.info
 	rm -rf lcov-html
-
--include $(OBJS:%.o=%.d)
diff --git a/hostapd/android.config b/hostapd/android.config
index be56ad5..f791daa 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -196,7 +196,7 @@
 #CONFIG_FST=y
 
 # Multiband Operation support
-# These extentions facilitate efficient use of multiple frequency bands
+# These extensions facilitate efficient use of multiple frequency bands
 # available to the AP and the devices that may associate with it.
 #CONFIG_MBO=y
 
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index cc1855d..ce32f3c 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -14,6 +14,7 @@
 #include "utils/common.h"
 #include "utils/uuid.h"
 #include "common/ieee802_11_defs.h"
+#include "common/sae.h"
 #include "crypto/sha256.h"
 #include "crypto/tls.h"
 #include "drivers/driver.h"
@@ -340,7 +341,7 @@
 			struct hostapd_radius_attr *attr, *a;
 			attr = hostapd_parse_radius_attr(buf + 19);
 			if (attr == NULL) {
-				wpa_printf(MSG_ERROR, "Invalid radius_auth_req_attr: %s",
+				wpa_printf(MSG_ERROR, "Invalid radius_accept_attr: %s",
 					   buf + 19);
 				user = NULL; /* already in the BSS list */
 				goto failed;
@@ -942,104 +943,6 @@
 }
 
 
-/* convert floats with one decimal place to value*10 int, i.e.,
- * "1.5" will return 15 */
-static int hostapd_config_read_int10(const char *value)
-{
-	int i, d;
-	char *pos;
-
-	i = atoi(value);
-	pos = os_strchr(value, '.');
-	d = 0;
-	if (pos) {
-		pos++;
-		if (*pos >= '0' && *pos <= '9')
-			d = *pos - '0';
-	}
-
-	return i * 10 + d;
-}
-
-
-static int valid_cw(int cw)
-{
-	return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
-		cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 ||
-		cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 ||
-		cw == 32767);
-}
-
-
-enum {
-	IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */
-	IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */
-	IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */
-	IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */
-};
-
-static int hostapd_config_tx_queue(struct hostapd_config *conf,
-				   const char *name, const char *val)
-{
-	int num;
-	const char *pos;
-	struct hostapd_tx_queue_params *queue;
-
-	/* skip 'tx_queue_' prefix */
-	pos = name + 9;
-	if (os_strncmp(pos, "data", 4) == 0 &&
-	    pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
-		num = pos[4] - '0';
-		pos += 6;
-	} else if (os_strncmp(pos, "after_beacon_", 13) == 0 ||
-		   os_strncmp(pos, "beacon_", 7) == 0) {
-		wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
-		return 0;
-	} else {
-		wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos);
-		return -1;
-	}
-
-	if (num >= NUM_TX_QUEUES) {
-		/* for backwards compatibility, do not trigger failure */
-		wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
-		return 0;
-	}
-
-	queue = &conf->tx_queue[num];
-
-	if (os_strcmp(pos, "aifs") == 0) {
-		queue->aifs = atoi(val);
-		if (queue->aifs < 0 || queue->aifs > 255) {
-			wpa_printf(MSG_ERROR, "Invalid AIFS value %d",
-				   queue->aifs);
-			return -1;
-		}
-	} else if (os_strcmp(pos, "cwmin") == 0) {
-		queue->cwmin = atoi(val);
-		if (!valid_cw(queue->cwmin)) {
-			wpa_printf(MSG_ERROR, "Invalid cwMin value %d",
-				   queue->cwmin);
-			return -1;
-		}
-	} else if (os_strcmp(pos, "cwmax") == 0) {
-		queue->cwmax = atoi(val);
-		if (!valid_cw(queue->cwmax)) {
-			wpa_printf(MSG_ERROR, "Invalid cwMax value %d",
-				   queue->cwmax);
-			return -1;
-		}
-	} else if (os_strcmp(pos, "burst") == 0) {
-		queue->burst = hostapd_config_read_int10(val);
-	} else {
-		wpa_printf(MSG_ERROR, "Unknown tx_queue field '%s'", pos);
-		return -1;
-	}
-
-	return 0;
-}
-
-
 #ifdef CONFIG_IEEE80211R_AP
 
 static int rkh_derive_key(const char *pos, u8 *key, size_t key_len)
@@ -2290,6 +2193,35 @@
 		pw->vlan_id = atoi(pos2);
 	}
 
+#ifdef CONFIG_SAE_PK
+	pos2 = os_strstr(pos, "|pk=");
+	if (pos2) {
+		const char *epos;
+		char *tmp;
+
+		if (!end)
+			end = pos2;
+		pos2 += 4;
+		epos = os_strchr(pos2, '|');
+		if (epos) {
+			tmp = os_malloc(epos - pos2 + 1);
+			if (!tmp)
+				goto fail;
+			os_memcpy(tmp, pos2, epos - pos2);
+			tmp[epos - pos2] = '\0';
+		} else {
+			tmp = os_strdup(pos2);
+			if (!tmp)
+				goto fail;
+		}
+
+		pw->pk = sae_parse_pk(tmp);
+		str_clear_free(tmp);
+		if (!pw->pk)
+			goto fail;
+	}
+#endif /* CONFIG_SAE_PK */
+
 	pos2 = os_strstr(pos, "|id=");
 	if (pos2) {
 		if (!end)
@@ -2312,6 +2244,18 @@
 		pw->password[end - val] = '\0';
 	}
 
+#ifdef CONFIG_SAE_PK
+	if (pw->pk &&
+#ifdef CONFIG_TESTING_OPTIONS
+	    !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+	    !sae_pk_valid_password(pw->password)) {
+		wpa_printf(MSG_INFO,
+			   "Invalid SAE password for a SAE-PK sae_password entry");
+		goto fail;
+	}
+#endif /* CONFIG_SAE_PK */
+
 	pw->next = bss->sae_passwords;
 	bss->sae_passwords = pw;
 
@@ -2319,6 +2263,9 @@
 fail:
 	str_clear_free(pw->password);
 	os_free(pw->identifier);
+#ifdef CONFIG_SAE_PK
+	sae_deinit_pk(pw->pk);
+#endif /* CONFIG_SAE_PK */
 	os_free(pw);
 	return -1;
 }
@@ -2612,7 +2559,7 @@
 	} else if (os_strcmp(buf, "eap_teap_auth") == 0) {
 		int val = atoi(pos);
 
-		if (val < 0 || val > 1) {
+		if (val < 0 || val > 2) {
 			wpa_printf(MSG_ERROR,
 				   "Line %d: Invalid eap_teap_auth value",
 				   line);
@@ -3383,7 +3330,7 @@
 	} else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
 		conf->ap_table_expiration_time = atoi(pos);
 	} else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
-		if (hostapd_config_tx_queue(conf, buf, pos)) {
+		if (hostapd_config_tx_queue(conf->tx_queue, buf, pos)) {
 			wpa_printf(MSG_ERROR, "Line %d: invalid TX queue item",
 				   line);
 			return 1;
@@ -4190,6 +4137,12 @@
 		bss->own_ie_override = tmp;
 	} else if (os_strcmp(buf, "sae_reflection_attack") == 0) {
 		bss->sae_reflection_attack = atoi(pos);
+	} else if (os_strcmp(buf, "sae_commit_status") == 0) {
+		bss->sae_commit_status = atoi(pos);
+	} else if (os_strcmp(buf, "sae_pk_omit") == 0) {
+		bss->sae_pk_omit = atoi(pos);
+	} else if (os_strcmp(buf, "sae_pk_password_check_skip") == 0) {
+		bss->sae_pk_password_check_skip = atoi(pos);
 	} else if (os_strcmp(buf, "sae_commit_override") == 0) {
 		wpabuf_free(bss->sae_commit_override);
 		bss->sae_commit_override = wpabuf_parse_bin(pos);
@@ -4217,6 +4170,20 @@
 		bss->skip_prune_assoc = atoi(pos);
 	} else if (os_strcmp(buf, "ft_rsnxe_used") == 0) {
 		bss->ft_rsnxe_used = atoi(pos);
+	} else if (os_strcmp(buf, "oci_freq_override_eapol_m3") == 0) {
+		bss->oci_freq_override_eapol_m3 = atoi(pos);
+	} else if (os_strcmp(buf, "oci_freq_override_eapol_g1") == 0) {
+		bss->oci_freq_override_eapol_g1 = atoi(pos);
+	} else if (os_strcmp(buf, "oci_freq_override_saquery_req") == 0) {
+		bss->oci_freq_override_saquery_req = atoi(pos);
+	} else if (os_strcmp(buf, "oci_freq_override_saquery_resp") == 0) {
+		bss->oci_freq_override_saquery_resp = atoi(pos);
+	} else if (os_strcmp(buf, "oci_freq_override_ft_assoc") == 0) {
+		bss->oci_freq_override_ft_assoc = atoi(pos);
+	} else if (os_strcmp(buf, "oci_freq_override_fils_assoc") == 0) {
+		bss->oci_freq_override_fils_assoc = atoi(pos);
+	} else if (os_strcmp(buf, "oci_freq_override_wnm_sleep") == 0) {
+		bss->oci_freq_override_wnm_sleep = atoi(pos);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_SAE
 	} else if (os_strcmp(buf, "sae_password") == 0) {
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index edc69f4..30fa47f 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -47,6 +47,7 @@
 #include "ap/ap_config.h"
 #include "ap/ieee802_1x.h"
 #include "ap/wpa_auth.h"
+#include "ap/pmksa_cache_auth.h"
 #include "ap/ieee802_11.h"
 #include "ap/sta_info.h"
 #include "ap/wps_hostapd.h"
@@ -1478,12 +1479,31 @@
 			   os_strcmp(cmd, "sae_pwe") == 0) {
 			if (hapd->started)
 				hostapd_setup_sae_pt(hapd->conf);
+		} else if (os_strcasecmp(cmd, "transition_disable") == 0) {
+			wpa_auth_set_transition_disable(hapd->wpa_auth,
+							hapd->conf->transition_disable);
 		}
 
 #ifdef CONFIG_TESTING_OPTIONS
 		if (os_strcmp(cmd, "ft_rsnxe_used") == 0)
 			wpa_auth_set_ft_rsnxe_used(hapd->wpa_auth,
 						   hapd->conf->ft_rsnxe_used);
+		else if (os_strcmp(cmd, "oci_freq_override_eapol_m3") == 0)
+			wpa_auth_set_ocv_override_freq(
+				hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_EAPOL_M3,
+				atoi(value));
+		else if (os_strcmp(cmd, "oci_freq_override_eapol_g1") == 0)
+			wpa_auth_set_ocv_override_freq(
+				hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_EAPOL_G1,
+				atoi(value));
+		else if (os_strcmp(cmd, "oci_freq_override_ft_assoc") == 0)
+			wpa_auth_set_ocv_override_freq(
+				hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_FT_ASSOC,
+				atoi(value));
+		else if (os_strcmp(cmd, "oci_freq_override_fils_assoc") == 0)
+			wpa_auth_set_ocv_override_freq(
+				hapd->wpa_auth,
+				WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC, atoi(value));
 #endif /* CONFIG_TESTING_OPTIONS */
 	}
 
@@ -1902,7 +1922,7 @@
 	if (ip.ip_hl != 5 || ip.ip_v != 4 ||
 	    ntohs(ip.ip_len) > HWSIM_IP_LEN) {
 		wpa_printf(MSG_DEBUG,
-			   "test data: RX - ignore unexpect IP header");
+			   "test data: RX - ignore unexpected IP header");
 		return;
 	}
 
@@ -2437,6 +2457,19 @@
 }
 
 
+static int hostapd_ctrl_get_pmksa_pmk(struct hostapd_data *hapd, const u8 *addr,
+				      char *buf, size_t buflen)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, addr, NULL);
+	if (!pmksa)
+		return -1;
+
+	return wpa_snprintf_hex(buf, buflen, pmksa->pmk, pmksa->pmk_len);
+}
+
+
 static int hostapd_ctrl_get_pmk(struct hostapd_data *hapd, const char *cmd,
 				char *buf, size_t buflen)
 {
@@ -2452,13 +2485,13 @@
 	if (!sta || !sta->wpa_sm) {
 		wpa_printf(MSG_DEBUG, "No STA WPA state machine for " MACSTR,
 			   MAC2STR(addr));
-		return -1;
+		return hostapd_ctrl_get_pmksa_pmk(hapd, addr, buf, buflen);
 	}
 	pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
-	if (!pmk) {
+	if (!pmk || !pmk_len) {
 		wpa_printf(MSG_DEBUG, "No PMK stored for " MACSTR,
 			   MAC2STR(addr));
-		return -1;
+		return hostapd_ctrl_get_pmksa_pmk(hapd, addr, buf, buflen);
 	}
 
 	return wpa_snprintf_hex(buf, buflen, pmk, pmk_len);
@@ -3735,6 +3768,14 @@
 		if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
 			reply_len = -1;
 #ifdef CONFIG_DPP2
+	} else if (os_strncmp(buf, "DPP_CONTROLLER_START ", 21) == 0) {
+		if (hostapd_dpp_controller_start(hapd, buf + 20) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "DPP_CONTROLLER_START") == 0) {
+		if (hostapd_dpp_controller_start(hapd, NULL) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "DPP_CONTROLLER_STOP") == 0) {
+		dpp_controller_stop(hapd->iface->interfaces->dpp);
 	} else if (os_strncmp(buf, "DPP_CHIRP ", 10) == 0) {
 		if (hostapd_dpp_chirp(hapd, buf + 9) < 0)
 			reply_len = -1;
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 2341765..11eed99 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -351,7 +351,7 @@
 #CONFIG_ACS=y
 
 # Multiband Operation support
-# These extentions facilitate efficient use of multiple frequency bands
+# These extensions facilitate efficient use of multiple frequency bands
 # available to the AP and the devices that may associate with it.
 #CONFIG_MBO=y
 
diff --git a/hostapd/hidl/1.3/hostapd.cpp b/hostapd/hidl/1.3/hostapd.cpp
index 6bbf3d9..6450a9f 100644
--- a/hostapd/hidl/1.3/hostapd.cpp
+++ b/hostapd/hidl/1.3/hostapd.cpp
@@ -406,7 +406,7 @@
 		if (((band & IHostapd::BandMask::BAND_5_GHZ) != 0)
 		    || ((band & IHostapd::BandMask::BAND_6_GHZ) != 0)) {
 			hw_mode_as_string = "hw_mode=any";
-			if (iface_params.V1_1.V1_0.channelParams.enableAcs) {
+			if (iface_params.V1_1.V1_0.hwModeParams.enable80211AC) {
 				ht_cap_vht_oper_chwidth_as_string =
 				    "ht_capab=[HT40+]\n"
 				    "vht_oper_chwidth=1";
@@ -418,7 +418,7 @@
 		if (((band & IHostapd::BandMask::BAND_5_GHZ) != 0)
 		    || ((band & IHostapd::BandMask::BAND_6_GHZ) != 0)) {
 			hw_mode_as_string = "hw_mode=a";
-			if (iface_params.V1_1.V1_0.channelParams.enableAcs) {
+			if (iface_params.V1_1.V1_0.hwModeParams.enable80211AC) {
 				ht_cap_vht_oper_chwidth_as_string =
 				    "ht_capab=[HT40+]\n"
 				    "vht_oper_chwidth=1";
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 9fae06d..bf89fbc 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1202,7 +1202,7 @@
 # should be unique across all issuing servers. In theory, this is a variable
 # length field, but due to some existing implementations requiring A-ID to be
 # 16 octets in length, it is strongly recommended to use that length for the
-# field to provid interoperability with deployed peer implementations. This
+# field to provide interoperability with deployed peer implementations. This
 # field is configured in hex format.
 #eap_fast_a_id=101112131415161718191a1b1c1d1e1f
 
@@ -1229,6 +1229,8 @@
 # EAP-TEAP authentication type
 # 0 = inner EAP (default)
 # 1 = Basic-Password-Auth
+# 2 = Do not require Phase 2 authentication if client can be authenticated
+#     during Phase 1
 #eap_teap_auth=0
 
 # EAP-TEAP authentication behavior when using PAC
@@ -1730,6 +1732,19 @@
 # Enabling this automatically also enables ieee80211w, if not yet enabled.
 # 0 = disabled (default)
 # 1 = enabled
+# 2 = enabled in workaround mode - Allow STA that claims OCV capability to
+#     connect even if the STA doesn't send OCI or negotiate PMF. This
+#     workaround is to improve interoperability with legacy STAs which are
+#     wrongly copying reserved bits of RSN capabilities from the AP's
+#     RSNE into (Re)Association Request frames. When this configuration is
+#     enabled, the AP considers STA is OCV capable only when the STA indicates
+#     MFP capability in (Re)Association Request frames and sends OCI in
+#     EAPOL-Key msg 2/4/FT Reassociation Request frame/FILS (Re)Association
+#     Request frame; otherwise, the AP disables OCV for the current connection
+#     with the STA. Enabling this workaround mode reduced OCV protection to
+#     some extend since it allows misbehavior to go through. As such, this
+#     should be enabled only if interoperability with misbehaving STAs is
+#     needed.
 #ocv=1
 
 # disable_pmksa_caching: Disable PMKSA caching
@@ -1761,7 +1776,7 @@
 # be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and
 # by optional password identifier (dot11RSNAConfigPasswordIdentifier). In
 # addition, an optional VLAN ID specification can be used to bind the station
-# to the specified VLAN whenver the specific SAE password entry is used.
+# to the specified VLAN whenever the specific SAE password entry is used.
 #
 # If the peer MAC address is not included or is set to the wildcard address
 # (ff:ff:ff:ff:ff:ff), the entry is available for any station to use. If a
@@ -1776,7 +1791,8 @@
 # special meaning of removing all previously added entries.
 #
 # sae_password uses the following encoding:
-#<password/credential>[|mac=<peer mac>][|vlanid=<VLAN ID>][|id=<identifier>]
+#<password/credential>[|mac=<peer mac>][|vlanid=<VLAN ID>]
+#[|pk=<m:ECPrivateKey-base64>][|id=<identifier>]
 # Examples:
 #sae_password=secret
 #sae_password=really secret|mac=ff:ff:ff:ff:ff:ff
@@ -1965,7 +1981,7 @@
 # Wildcard entry:
 # Upon receiving a response from R0KH, it will be added to this list, so
 # subsequent requests won't be broadcast. If R0KH does not reply, it will be
-# blacklisted.
+# temporarily blocked (see rkh_neg_timeout).
 #r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff
 
 # List of R1KHs in the same Mobility Domain
@@ -2021,7 +2037,7 @@
 #ft_psk_generate_local=0
 
 ##### Neighbor table ##########################################################
-# Maximum number of entries kept in AP table (either for neigbor table or for
+# Maximum number of entries kept in AP table (either for neighbor table or for
 # detecting Overlapping Legacy BSS Condition). The oldest entry will be
 # removed when adding a new entry that would make the list grow over this
 # limit. Note! WFA certification for IEEE 802.11g requires that OLBC is
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 440664e..ad43981 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -975,7 +975,7 @@
 	dir = opendir(ctrl_iface_dir);
 	if (dir == NULL) {
 		printf("Control interface directory '%s' could not be "
-		       "openned.\n", ctrl_iface_dir);
+		       "opened.\n", ctrl_iface_dir);
 		return;
 	}
 
@@ -1402,6 +1402,13 @@
 }
 
 
+static int hostapd_cli_cmd_dpp_bootstrap_set(struct wpa_ctrl *ctrl, int argc,
+					     char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_SET", 1, argc, argv);
+}
+
+
 static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
 					 char *argv[])
 {
@@ -1464,6 +1471,23 @@
 	return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
 }
 
+
+#ifdef CONFIG_DPP2
+
+static int hostapd_cli_cmd_dpp_chirp(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return hostapd_cli_cmd(ctrl, "DPP_CHIRP", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "DPP_STOP_CHIRP");
+}
+
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 
 
@@ -1650,6 +1674,8 @@
 	  "<id> = get DPP bootstrap URI" },
 	{ "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL,
 	  "<id> = show DPP bootstrap information" },
+	{ "dpp_bootstrap_set", hostapd_cli_cmd_dpp_bootstrap_set, NULL,
+	  "<id> [conf=..] [ssid=<SSID>] [ssid_charset=#] [psk=<PSK>] [pass=<passphrase>] [configurator=<id>] [conn_status=#] [akm_use_selector=<0|1>] [group_id=..] [expiry=#] [csrattrs=..] = set DPP configurator parameters" },
 	{ "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL,
 	  "peer=<id> [own=<id>] = initiate DPP bootstrapping" },
 	{ "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL,
@@ -1670,6 +1696,12 @@
 	  "add PKEX code" },
 	{ "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL,
 	  "*|<id> = remove DPP pkex information" },
+#ifdef CONFIG_DPP2
+	{ "dpp_chirp", hostapd_cli_cmd_dpp_chirp, NULL,
+	  "own=<BI ID> iter=<count> = start DPP chirp" },
+	{ "dpp_stop_chirp", hostapd_cli_cmd_dpp_stop_chirp, NULL,
+	  "= stop DPP chirp" },
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 	{ "accept_acl", hostapd_cli_cmd_accept_macacl, NULL,
 	  "=Add/Delete/Show/Clear accept MAC ACL" },
diff --git a/hostapd/main.c b/hostapd/main.c
index cdcb692..346df48 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -677,7 +677,6 @@
 #ifdef CONFIG_DPP
 	os_memset(&dpp_conf, 0, sizeof(dpp_conf));
 	dpp_conf.cb_ctx = &interfaces;
-	/* TODO: dpp_conf.msg_ctx? */
 #ifdef CONFIG_DPP2
 	dpp_conf.remove_bi = hostapd_dpp_remove_bi;
 #endif /* CONFIG_DPP2 */
diff --git a/hostapd/sae_pk_gen.c b/hostapd/sae_pk_gen.c
new file mode 100644
index 0000000..c31eff7
--- /dev/null
+++ b/hostapd/sae_pk_gen.c
@@ -0,0 +1,196 @@
+/*
+ * SAE-PK password/modifier generator
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "crypto/crypto.h"
+#include "common/sae.h"
+
+
+int main(int argc, char *argv[])
+{
+	char *der = NULL;
+	size_t der_len;
+	struct crypto_ec_key *key = NULL;
+	struct wpabuf *pub = NULL;
+	u8 *data = NULL, *m;
+	size_t data_len;
+	char *b64 = NULL, *pw = NULL, *pos, *src;
+	int sec, j;
+	int ret = -1;
+	u8 hash[SAE_MAX_HASH_LEN];
+	char hash_hex[2 * SAE_MAX_HASH_LEN + 1];
+	u8 pw_base_bin[SAE_MAX_HASH_LEN];
+	u8 *dst;
+	int group;
+	size_t hash_len;
+	unsigned long long i, expected;
+	char m_hex[2 * SAE_PK_M_LEN + 1];
+	u32 sec_1b, val20;
+
+	wpa_debug_level = MSG_INFO;
+	if (os_program_init() < 0)
+		goto fail;
+
+	if (argc != 4) {
+		fprintf(stderr,
+			"usage: sae_pk_gen <DER ECPrivateKey file> <Sec:3|5> <SSID>\n");
+		goto fail;
+	}
+
+	sec = atoi(argv[2]);
+	if (sec != 3 && sec != 5) {
+		fprintf(stderr,
+			"Invalid Sec value (allowed values: 3 and 5)\n");
+		goto fail;
+	}
+	sec_1b = sec == 3;
+	expected = 1;
+	for (j = 0; j < sec; j++)
+		expected *= 256;
+
+	der = os_readfile(argv[1], &der_len);
+	if (!der) {
+		fprintf(stderr, "Could not read %s: %s\n",
+			argv[1], strerror(errno));
+		goto fail;
+	}
+
+	key = crypto_ec_key_parse_priv((u8 *) der, der_len);
+	if (!key) {
+		fprintf(stderr, "Could not parse ECPrivateKey\n");
+		goto fail;
+	}
+
+	pub = crypto_ec_key_get_subject_public_key(key);
+	if (!pub) {
+		fprintf(stderr, "Failed to build SubjectPublicKey\n");
+		goto fail;
+	}
+
+	group = crypto_ec_key_group(key);
+	switch (group) {
+	case 19:
+		hash_len = 32;
+		break;
+	case 20:
+		hash_len = 48;
+		break;
+	case 21:
+		hash_len = 64;
+		break;
+	default:
+		fprintf(stderr, "Unsupported private key group\n");
+		goto fail;
+	}
+
+	data_len = os_strlen(argv[3]) + SAE_PK_M_LEN + wpabuf_len(pub);
+	data = os_malloc(data_len);
+	if (!data) {
+		fprintf(stderr, "No memory for data buffer\n");
+		goto fail;
+	}
+	os_memcpy(data, argv[3], os_strlen(argv[3]));
+	m = data + os_strlen(argv[3]);
+	if (os_get_random(m, SAE_PK_M_LEN) < 0) {
+		fprintf(stderr, "Could not generate random Modifier M\n");
+		goto fail;
+	}
+	os_memcpy(m + SAE_PK_M_LEN, wpabuf_head(pub), wpabuf_len(pub));
+
+	fprintf(stderr, "Searching for a suitable Modifier M value\n");
+	for (i = 0;; i++) {
+		if (sae_hash(hash_len, data, data_len, hash) < 0) {
+			fprintf(stderr, "Hash failed\n");
+			goto fail;
+		}
+		if (hash[0] == 0 && hash[1] == 0) {
+			if ((hash[2] & 0xf0) == 0)
+				fprintf(stderr, "\r%3.2f%%",
+					100.0 * (double) i / (double) expected);
+			for (j = 2; j < sec; j++) {
+				if (hash[j])
+					break;
+			}
+			if (j == sec)
+				break;
+		}
+		inc_byte_array(m, SAE_PK_M_LEN);
+	}
+
+	if (wpa_snprintf_hex(m_hex, sizeof(m_hex), m, SAE_PK_M_LEN) < 0 ||
+	    wpa_snprintf_hex(hash_hex, sizeof(hash_hex), hash, hash_len) < 0)
+		goto fail;
+	fprintf(stderr, "\nFound a valid hash in %llu iterations: %s\n",
+		i + 1, hash_hex);
+
+	b64 = base64_encode(der, der_len, NULL);
+	if (!b64)
+		goto fail;
+	src = pos = b64;
+	while (*src) {
+		if (*src != '\n')
+			*pos++ = *src;
+		src++;
+	}
+	*pos = '\0';
+
+	/* Skip 8*Sec bits and add Sec_1b as the every 20th bit starting with
+	 * one. */
+	os_memset(pw_base_bin, 0, sizeof(pw_base_bin));
+	dst = pw_base_bin;
+	for (j = 0; j < 8 * (int) hash_len / 20; j++) {
+		val20 = sae_pk_get_be19(hash + sec);
+		val20 |= sec_1b << 19;
+		sae_pk_buf_shift_left_19(hash + sec, hash_len - sec);
+
+		if (j & 1) {
+			*dst |= (val20 >> 16) & 0x0f;
+			dst++;
+			*dst++ = (val20 >> 8) & 0xff;
+			*dst++ = val20 & 0xff;
+		} else {
+			*dst++ = (val20 >> 12) & 0xff;
+			*dst++ = (val20 >> 4) & 0xff;
+			*dst = (val20 << 4) & 0xf0;
+		}
+	}
+	if (wpa_snprintf_hex(hash_hex, sizeof(hash_hex),
+			     pw_base_bin, hash_len - sec) >= 0)
+		fprintf(stderr, "PasswordBase binary data for base32: %s",
+			hash_hex);
+
+	pw = sae_pk_base32_encode(pw_base_bin, 20 * 3 - 5);
+	if (!pw)
+		goto fail;
+
+	printf("# SAE-PK password/M/private key for Sec=%d.\n", sec);
+	printf("sae_password=%s|pk=%s:%s\n", pw, m_hex, b64);
+	printf("# Longer passwords can be used for improved security at the cost of usability:\n");
+	for (j = 4; j <= ((int) hash_len * 8 + 5 - 8 * sec) / 19; j++) {
+		os_free(pw);
+		pw = sae_pk_base32_encode(pw_base_bin, 20 * j - 5);
+		if (pw)
+			printf("# %s\n", pw);
+	}
+
+	ret = 0;
+fail:
+	os_free(der);
+	wpabuf_free(pub);
+	crypto_ec_key_deinit(key);
+	os_free(data);
+	os_free(b64);
+	os_free(pw);
+
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
index cc2af03..4dcfe2d 100644
--- a/hs20/client/Makefile
+++ b/hs20/client/Makefile
@@ -1,28 +1,6 @@
-all: hs20-osu-client
+ALL=hs20-osu-client
 
-ifndef CC
-CC=gcc
-endif
-
-ifndef LDO
-LDO=$(CC)
-endif
-
-ifeq ($(QUIET), 1)
-Q=@
-E=true
-else
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-endif
-
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
+include ../../src/build.rules
 
 CFLAGS += -I../../src/utils
 CFLAGS += -I../../src/common
@@ -93,23 +71,11 @@
 OBJS += ../../src/crypto/tls_openssl_ocsp.o
 LIBS += -lssl -lcrypto
 
+_OBJS_VAR := OBJS
+include ../../src/objs.mk
 hs20-osu-client: $(OBJS)
 	$(Q)$(LDO) $(LDFLAGS) -o hs20-osu-client $(OBJS) $(LIBS)
 	@$(E) "  LD " $@
 
-%.o: %.c
-	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
-	@$(E) "  CC " $<
-
-clean:
-	rm -f core *~ *.o *.d hs20-osu-client
-	rm -f ../../src/utils/*.o
-	rm -f ../../src/utils/*.d
-	rm -f ../../src/common/*.o
-	rm -f ../../src/common/*.d
-	rm -f ../../src/crypto/*.o
-	rm -f ../../src/crypto/*.d
-	rm -f ../../src/wps/*.o
-	rm -f ../../src/wps/*.d
-
--include $(OBJS:%.o=%.d)
+clean: common-clean
+	rm -f core *~
diff --git a/hs20/server/.gitignore b/hs20/server/.gitignore
new file mode 100644
index 0000000..fecb096
--- /dev/null
+++ b/hs20/server/.gitignore
@@ -0,0 +1 @@
+hs20_spp_server
diff --git a/hs20/server/Makefile b/hs20/server/Makefile
index 9b73727..0cab6d6 100644
--- a/hs20/server/Makefile
+++ b/hs20/server/Makefile
@@ -1,16 +1,6 @@
-all: hs20_spp_server
+ALL=hs20_spp_server
 
-ifndef CC
-CC=gcc
-endif
-
-ifndef LDO
-LDO=$(CC)
-endif
-
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
+include ../../src/build.rules
 
 CFLAGS += -I../../src
 CFLAGS += -I../../src/utils
@@ -43,14 +33,10 @@
 LIBS += $(shell xml2-config --libs)
 OBJS += ../../src/utils/xml_libxml2.o
 
+_OBJS_VAR := OBJS
+include ../../src/objs.mk
 hs20_spp_server: $(OBJS)
 	$(LDO) $(LDFLAGS) -o hs20_spp_server $(OBJS) $(LIBS)
 
-clean:
-	rm -f core *~ *.o *.d hs20_spp_server
-	rm -f ../../src/utils/*.o
-	rm -f ../../src/utils/*.d
-	rm -f ../../src/crypto/*.o
-	rm -f ../../src/crypto/*.d
-
--include $(OBJS:%.o=%.d)
+clean: common-clean
+	rm -f core *~
diff --git a/src/Makefile b/src/Makefile
index c9e84c1..6eb7f2a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -5,8 +5,8 @@
 	for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
 
 clean:
-	for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
-	rm -f *~
+	$(Q)for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
+	$(Q)rm -f *~
 
 install:
 	for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 54e48a0..a1e9b7c 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -1,13 +1,3 @@
-all: libap.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libap.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 CFLAGS += -DHOSTAPD
 CFLAGS += -DNEED_AP_MLME
 CFLAGS += -DCONFIG_ETH_P_OUI
@@ -67,7 +57,4 @@
 	wps_hostapd.o \
 	x_snoop.o
 
-libap.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 35a32a1..04535a1 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -160,6 +160,10 @@
 
 	/* Default to strict CRL checking. */
 	bss->check_crl_strict = 1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	bss->sae_commit_status = -1;
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
@@ -461,7 +465,8 @@
 	struct hostapd_ssid *ssid = &conf->ssid;
 	struct sae_password_entry *pw;
 
-	if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf)) ||
+	if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf) &&
+	     !hostapd_sae_pk_in_use(conf)) ||
 	    conf->sae_pwe == 3 ||
 	    !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
 		return 0; /* PT not needed */
@@ -711,6 +716,9 @@
 #ifdef CONFIG_SAE
 		sae_deinit_pt(tmp->pt);
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_SAE_PK
+		sae_deinit_pk(tmp->pk);
+#endif /* CONFIG_SAE_PK */
 		os_free(tmp);
 	}
 }
@@ -1111,6 +1119,37 @@
 }
 
 
+#ifdef CONFIG_SAE_PK
+static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss)
+{
+	struct sae_password_entry *pw;
+	bool res = false;
+
+	if (bss->ssid.wpa_passphrase &&
+#ifdef CONFIG_TESTING_OPTIONS
+	    !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+	    sae_pk_valid_password(bss->ssid.wpa_passphrase))
+		res = true;
+
+	for (pw = bss->sae_passwords; pw; pw = pw->next) {
+		if (!pw->pk &&
+#ifdef CONFIG_TESTING_OPTIONS
+		    !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+		    sae_pk_valid_password(pw->password))
+			return true;
+
+		if (bss->ssid.wpa_passphrase && res && pw->pk &&
+		    os_strcmp(bss->ssid.wpa_passphrase, pw->password) == 0)
+			res = false;
+	}
+
+	return res;
+}
+#endif /* CONFIG_SAE_PK */
+
+
 static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
 				    struct hostapd_config *conf,
 				    int full_config)
@@ -1294,6 +1333,15 @@
 	}
 #endif /* CONFIG_OCV */
 
+#ifdef CONFIG_SAE_PK
+	if (full_config && hostapd_sae_pk_in_use(bss) &&
+	    hostapd_sae_pk_password_without_pk(bss)) {
+		wpa_printf(MSG_ERROR,
+			   "SAE-PK: SAE password uses SAE-PK style, but does not have PK configured");
+		return -1;
+	}
+#endif /* CONFIG_SAE_PK */
+
 	return 0;
 }
 
@@ -1473,3 +1521,38 @@
 		return 2;
 	return with_id;
 }
+
+
+bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_SAE_PK
+	struct sae_password_entry *pw;
+
+	for (pw = conf->sae_passwords; pw; pw = pw->next) {
+		if (pw->pk)
+			return true;
+	}
+#endif /* CONFIG_SAE_PK */
+
+	return false;
+}
+
+
+#ifdef CONFIG_SAE_PK
+bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf)
+{
+	bool with_pk = false;
+	struct sae_password_entry *pw;
+
+	if (conf->ssid.wpa_passphrase)
+		return false;
+
+	for (pw = conf->sae_passwords; pw; pw = pw->next) {
+		if (!pw->pk)
+			return false;
+		with_pk = true;
+	}
+
+	return with_pk;
+}
+#endif /* CONFIG_SAE_PK */
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index cffa636..bada04c 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -197,15 +197,6 @@
 
 
 #define NUM_TX_QUEUES 4
-
-struct hostapd_tx_queue_params {
-	int aifs;
-	int cwmin;
-	int cwmax;
-	int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
-};
-
-
 #define MAX_ROAMING_CONSORTIUM_LEN 15
 
 struct hostapd_roaming_consortium {
@@ -261,6 +252,7 @@
 	u8 peer_addr[ETH_ALEN];
 	int vlan_id;
 	struct sae_pt *pt;
+	struct sae_pk *pk;
 };
 
 struct dpp_controller_conf {
@@ -677,6 +669,9 @@
 	u8 bss_load_test_set;
 	struct wpabuf *own_ie_override;
 	int sae_reflection_attack;
+	int sae_commit_status;
+	int sae_pk_omit;
+	int sae_pk_password_check_skip;
 	struct wpabuf *sae_commit_override;
 	struct wpabuf *rsne_override_eapol;
 	struct wpabuf *rsnxe_override_eapol;
@@ -687,6 +682,13 @@
 	int no_beacon_rsnxe;
 	int skip_prune_assoc;
 	int ft_rsnxe_used;
+	unsigned int oci_freq_override_eapol_m3;
+	unsigned int oci_freq_override_eapol_g1;
+	unsigned int oci_freq_override_saquery_req;
+	unsigned int oci_freq_override_saquery_resp;
+	unsigned int oci_freq_override_ft_assoc;
+	unsigned int oci_freq_override_fils_assoc;
+	unsigned int oci_freq_override_wnm_sleep;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #define MESH_ENABLED BIT(0)
@@ -1140,6 +1142,8 @@
 void hostapd_set_security_params(struct hostapd_bss_config *bss,
 				 int full_config);
 int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf);
+bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf);
+bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf);
 int hostapd_setup_sae_pt(struct hostapd_bss_config *conf);
 
 #endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 22e672c..b3b33b7 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -560,10 +560,13 @@
 		pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
 		pos = hostapd_eid_vht_operation(hapd, pos);
 		pos = hostapd_eid_txpower_envelope(hapd, pos);
-		pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
 	}
 #endif /* CONFIG_IEEE80211AC */
 
+	if ((hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) ||
+	    hapd->iconf->ieee80211ax)
+		pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+
 	pos = hostapd_eid_fils_indic(hapd, pos, 0);
 	pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
 
@@ -1281,10 +1284,13 @@
 		tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0);
 		tailpos = hostapd_eid_vht_operation(hapd, tailpos);
 		tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
-		tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
 	}
 #endif /* CONFIG_IEEE80211AC */
 
+	if ((hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) ||
+	     hapd->iconf->ieee80211ax)
+		tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
+
 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
 	tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
 
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 3c078b9..f04a00a 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -955,10 +955,13 @@
 		if (*skip_radar) {
 			*skip_radar = 0;
 		} else {
-			if (iface->conf->vht_oper_chwidth == CHANWIDTH_USE_HT)
+			int oper_chwidth;
+
+			oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+			if (oper_chwidth == CHANWIDTH_USE_HT)
 				break;
 			*skip_radar = 1;
-			iface->conf->vht_oper_chwidth--;
+			hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1);
 		}
 	}
 
@@ -1028,7 +1031,7 @@
 	unsigned int i;
 	int err = 1;
 	struct hostapd_hw_modes *cmode = iface->current_mode;
-	u8 current_vht_oper_chwidth = iface->conf->vht_oper_chwidth;
+	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
 
 	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
 		   __func__, iface->cac_started ? "yes" : "no",
@@ -1089,8 +1092,8 @@
 		"freq=%d chan=%d sec_chan=%d", channel->freq,
 		channel->chan, secondary_channel);
 
-	new_vht_oper_chwidth = iface->conf->vht_oper_chwidth;
-	iface->conf->vht_oper_chwidth = current_vht_oper_chwidth;
+	new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+	hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
 
 	/* Setup CSA request */
 	os_memset(&csa_settings, 0, sizeof(csa_settings));
@@ -1130,7 +1133,7 @@
 		iface->freq = channel->freq;
 		iface->conf->channel = channel->chan;
 		iface->conf->secondary_channel = secondary_channel;
-		iface->conf->vht_oper_chwidth = new_vht_oper_chwidth;
+		hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
 		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
 						     oper_centr_freq_seg0_idx);
 		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 178fd12..6772a87 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -29,6 +29,9 @@
 #ifdef CONFIG_DPP2
 static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
 						    void *timeout_ctx);
+static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
+					  struct dpp_authentication *auth,
+					  struct dpp_config_obj *conf);
 #endif /* CONFIG_DPP2 */
 
 static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -63,6 +66,10 @@
 					wpabuf_len(hapd->dpp_auth->resp_msg));
 	}
 
+#ifdef CONFIG_DPP2
+	dpp_controller_new_qr_code(hapd->iface->interfaces->dpp, bi);
+#endif /* CONFIG_DPP2 */
+
 	return bi->id;
 }
 
@@ -486,12 +493,35 @@
 }
 
 
+#ifdef CONFIG_DPP2
+static int hostapd_dpp_process_conf_obj(void *ctx,
+				     struct dpp_authentication *auth)
+{
+	struct hostapd_data *hapd = ctx;
+	unsigned int i;
+
+	for (i = 0; i < auth->num_conf_obj; i++)
+		hostapd_dpp_handle_config_obj(hapd, auth,
+					      &auth->conf_obj[i]);
+
+	return 0;
+}
+#endif /* CONFIG_DPP2 */
+
+
 int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
 {
 	const char *pos;
 	struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+	struct dpp_authentication *auth;
 	u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
 	unsigned int neg_freq = 0;
+	int tcp = 0;
+#ifdef CONFIG_DPP2
+	int tcp_port = DPP_TCP_PORT;
+	struct hostapd_ip_addr ipaddr;
+	char *addr;
+#endif /* CONFIG_DPP2 */
 
 	pos = os_strstr(cmd, " peer=");
 	if (!pos)
@@ -504,6 +534,25 @@
 		return -1;
 	}
 
+#ifdef CONFIG_DPP2
+	pos = os_strstr(cmd, " tcp_port=");
+	if (pos) {
+		pos += 10;
+		tcp_port = atoi(pos);
+	}
+
+	addr = get_param(cmd, " tcp_addr=");
+	if (addr) {
+		int res;
+
+		res = hostapd_parse_ip_addr(addr, &ipaddr);
+		os_free(addr);
+		if (res)
+			return -1;
+		tcp = 1;
+	}
+#endif /* CONFIG_DPP2 */
+
 	pos = os_strstr(cmd, " own=");
 	if (pos) {
 		pos += 5;
@@ -541,7 +590,7 @@
 	if (pos)
 		neg_freq = atoi(pos + 10);
 
-	if (hapd->dpp_auth) {
+	if (!tcp && hapd->dpp_auth) {
 		eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
 		eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
 				     hapd, NULL);
@@ -555,26 +604,32 @@
 		dpp_auth_deinit(hapd->dpp_auth);
 	}
 
-	hapd->dpp_auth = dpp_auth_init(hapd->iface->interfaces->dpp,
-				       hapd->msg_ctx, peer_bi, own_bi,
-				       allowed_roles, neg_freq,
-				       hapd->iface->hw_features,
-				       hapd->iface->num_hw_features);
-	if (!hapd->dpp_auth)
+	auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+			     peer_bi, own_bi, allowed_roles, neg_freq,
+			     hapd->iface->hw_features,
+			     hapd->iface->num_hw_features);
+	if (!auth)
 		goto fail;
-	hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
-	if (dpp_set_configurator(hapd->dpp_auth, cmd) < 0) {
-		dpp_auth_deinit(hapd->dpp_auth);
-		hapd->dpp_auth = NULL;
+	hostapd_dpp_set_testing_options(hapd, auth);
+	if (dpp_set_configurator(auth, cmd) < 0) {
+		dpp_auth_deinit(auth);
 		goto fail;
 	}
 
-	hapd->dpp_auth->neg_freq = neg_freq;
+	auth->neg_freq = neg_freq;
 
 	if (!is_zero_ether_addr(peer_bi->mac_addr))
-		os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
-			  ETH_ALEN);
+		os_memcpy(auth->peer_mac_addr, peer_bi->mac_addr, ETH_ALEN);
 
+#ifdef CONFIG_DPP2
+	if (tcp)
+		return dpp_tcp_init(hapd->iface->interfaces->dpp, auth,
+				    &ipaddr, tcp_port, hapd->conf->dpp_name,
+				    DPP_NETROLE_AP, hapd->msg_ctx, hapd,
+				    hostapd_dpp_process_conf_obj);
+#endif /* CONFIG_DPP2 */
+
+	hapd->dpp_auth = auth;
 	return hostapd_dpp_auth_init_next(hapd);
 fail:
 	return -1;
@@ -1173,6 +1228,9 @@
 		    r_bootstrap, r_bootstrap_len);
 	peer_bi = dpp_bootstrap_find_chirp(hapd->iface->interfaces->dpp,
 					   r_bootstrap);
+	dpp_notify_chirp_received(hapd->msg_ctx,
+				  peer_bi ? (int) peer_bi->id : -1,
+				  src, freq, r_bootstrap);
 	if (!peer_bi) {
 		if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
 					src, hdr, buf, len, freq, NULL,
@@ -1194,8 +1252,8 @@
 			     0);
 	if (!auth)
 		return;
-	hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
-	if (dpp_set_configurator(hapd->dpp_auth,
+	hostapd_dpp_set_testing_options(hapd, auth);
+	if (dpp_set_configurator(auth,
 				 hapd->dpp_configurator_params) < 0) {
 		dpp_auth_deinit(auth);
 		return;
@@ -1235,11 +1293,12 @@
 				     const u8 *hdr, const u8 *buf, size_t len,
 				     unsigned int freq)
 {
-	const u8 *csign_hash;
-	u16 csign_hash_len;
+	const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
+	u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
 	struct dpp_configurator *conf;
 	struct dpp_authentication *auth;
 	unsigned int wait_time, max_wait_time;
+	u16 group;
 
 	if (hapd->dpp_auth) {
 		wpa_printf(MSG_DEBUG,
@@ -1271,8 +1330,22 @@
 		return;
 	}
 
+	fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+			       &fcgroup_len);
+	if (!fcgroup || fcgroup_len != 2) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Finite Cyclic Group attribute");
+		return;
+	}
+	group = WPA_GET_LE16(fcgroup);
+	wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
+
+	a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
+	e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
+
 	auth = dpp_reconfig_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
-				 conf, freq);
+				 conf, freq, group, a_nonce, a_nonce_len,
+				 e_id, e_id_len);
 	if (!auth)
 		return;
 	hostapd_dpp_set_testing_options(hapd, auth);
@@ -2156,6 +2229,45 @@
 
 #ifdef CONFIG_DPP2
 
+int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_controller_config config;
+	const char *pos;
+
+	os_memset(&config, 0, sizeof(config));
+	config.allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
+	config.netrole = DPP_NETROLE_AP;
+	config.msg_ctx = hapd->msg_ctx;
+	config.cb_ctx = hapd;
+	config.process_conf_obj = hostapd_dpp_process_conf_obj;
+	if (cmd) {
+		pos = os_strstr(cmd, " tcp_port=");
+		if (pos) {
+			pos += 10;
+			config.tcp_port = atoi(pos);
+		}
+
+		pos = os_strstr(cmd, " role=");
+		if (pos) {
+			pos += 6;
+			if (os_strncmp(pos, "configurator", 12) == 0)
+				config.allowed_roles = DPP_CAPAB_CONFIGURATOR;
+			else if (os_strncmp(pos, "enrollee", 8) == 0)
+				config.allowed_roles = DPP_CAPAB_ENROLLEE;
+			else if (os_strncmp(pos, "either", 6) == 0)
+				config.allowed_roles = DPP_CAPAB_CONFIGURATOR |
+					DPP_CAPAB_ENROLLEE;
+			else
+				return -1;
+		}
+
+		config.qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+	}
+	config.configurator_params = hapd->dpp_configurator_params;
+	return dpp_controller_start(hapd->iface->interfaces->dpp, &config);
+}
+
+
 static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx);
 
 static void hostapd_dpp_chirp_timeout(void *eloop_ctx, void *timeout_ctx)
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index 7e74185..264d3e4 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -41,6 +41,7 @@
 void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
 void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
 
+int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd);
 void hostapd_dpp_chirp_stop(struct hostapd_data *hapd);
 void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 524a151..9af5445 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -105,6 +105,32 @@
 #endif /* CONFIG_FILS */
 
 
+static bool check_sa_query_need(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if ((sta->flags &
+	     (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
+	    (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
+		return false;
+
+	if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
+		ap_check_sa_query_timeout(hapd, sta);
+
+	if (!sta->sa_query_timed_out && (sta->auth_alg != WLAN_AUTH_FT)) {
+		/*
+		 * STA has already been associated with MFP and SA Query timeout
+		 * has not been reached. Reject the association attempt
+		 * temporarily and start SA Query, if one is not pending.
+		 */
+		if (sta->sa_query_count == 0)
+			ap_sta_start_sa_query(hapd, sta);
+
+		return true;
+	}
+
+	return false;
+}
+
+
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 			const u8 *req_ies, size_t req_ies_len, int reassoc)
 {
@@ -293,6 +319,17 @@
 		    os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
 			struct wpabuf *wps;
 
+			if (check_sa_query_need(hapd, sta)) {
+				status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+				p = hostapd_eid_assoc_comeback_time(hapd, sta,
+								    p);
+
+				hostapd_sta_assoc(hapd, addr, reassoc, status,
+						  buf, p - buf);
+				return 0;
+			}
+
 			sta->flags |= WLAN_STA_WPS;
 			wps = ieee802_11_vendor_ie_concat(ie, ielen,
 							  WPS_IE_VENDOR_TYPE);
@@ -388,25 +425,7 @@
 			goto fail;
 		}
 
-		if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
-		    (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
-		    !sta->sa_query_timed_out &&
-		    sta->sa_query_count > 0)
-			ap_check_sa_query_timeout(hapd, sta);
-		if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
-		    (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
-		    !sta->sa_query_timed_out &&
-		    (sta->auth_alg != WLAN_AUTH_FT)) {
-			/*
-			 * STA has already been associated with MFP and SA
-			 * Query timeout has not been reached. Reject the
-			 * association attempt temporarily and start SA Query,
-			 * if one is not pending.
-			 */
-
-			if (sta->sa_query_count == 0)
-				ap_sta_start_sa_query(hapd, sta);
-
+		if (check_sa_query_need(hapd, sta)) {
 			status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
 
 			p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
@@ -439,7 +458,7 @@
 #ifdef CONFIG_SAE
 		if (hapd->conf->sae_pwe == 2 &&
 		    sta->auth_alg == WLAN_AUTH_SAE &&
-		    sta->sae && sta->sae->tmp && !sta->sae->tmp->h2e &&
+		    sta->sae && !sta->sae->h2e &&
 		    elems.rsnxe && elems.rsnxe_len >= 1 &&
 		    (elems.rsnxe[0] & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) {
 			wpa_printf(MSG_INFO, "SAE: " MACSTR
@@ -844,8 +863,6 @@
 			     int offset, int width, int cf1, int cf2,
 			     int finished)
 {
-	/* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */
-
 #ifdef NEED_AP_MLME
 	int channel, chwidth, is_dfs;
 	u8 seg0_idx = 0, seg1_idx = 0;
@@ -895,9 +912,18 @@
 
 	switch (hapd->iface->current_mode->mode) {
 	case HOSTAPD_MODE_IEEE80211A:
-		if (cf1 > 5000)
+		if (cf1 == 5935)
+			seg0_idx = (cf1 - 5925) / 5;
+		else if (cf1 > 5950)
+			seg0_idx = (cf1 - 5950) / 5;
+		else if (cf1 > 5000)
 			seg0_idx = (cf1 - 5000) / 5;
-		if (cf2 > 5000)
+
+		if (cf2 == 5935)
+			seg1_idx = (cf2 - 5925) / 5;
+		else if (cf2 > 5950)
+			seg1_idx = (cf2 - 5950) / 5;
+		else if (cf2 > 5000)
 			seg1_idx = (cf2 - 5000) / 5;
 		break;
 	default:
@@ -958,6 +984,29 @@
 
 	for (i = 0; i < hapd->iface->num_bss; i++)
 		hostapd_neighbor_set_own_report(hapd->iface->bss[i]);
+
+#ifdef CONFIG_OCV
+	if (hapd->conf->ocv) {
+		struct sta_info *sta;
+		bool check_sa_query = false;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (wpa_auth_uses_ocv(sta->wpa_sm) &&
+			    !(sta->flags & WLAN_STA_WNM_SLEEP_MODE)) {
+				sta->post_csa_sa_query = 1;
+				check_sa_query = true;
+			}
+		}
+
+		if (check_sa_query) {
+			wpa_printf(MSG_DEBUG,
+				   "OCV: Check post-CSA SA Query initiation in 15 seconds");
+			eloop_register_timeout(15, 0,
+					       hostapd_ocv_check_csa_sa_query,
+					       hapd, NULL);
+		}
+	}
+#endif /* CONFIG_OCV */
 #endif /* NEED_AP_MLME */
 }
 
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 9567e20..90f1577 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1555,11 +1555,14 @@
 			di->prot = prot;
 			di->sd_resp = buf;
 			di->sd_resp_pos = 0;
+			di->dpp = 1;
 			tx_buf = gas_build_initial_resp(
 				dialog_token, WLAN_STATUS_SUCCESS,
-				comeback_delay, 10);
-			if (tx_buf)
+				comeback_delay, 10 + 2);
+			if (tx_buf) {
 				gas_serv_write_dpp_adv_proto(tx_buf);
+				wpabuf_put_le16(tx_buf, 0);
+			}
 		}
 	} else {
 		wpa_printf(MSG_DEBUG,
@@ -1782,9 +1785,10 @@
 		tx_buf = gas_build_comeback_resp(dialog_token,
 						 WLAN_STATUS_SUCCESS,
 						 dialog->sd_frag_id, more, 0,
-						 10 + frag_len);
+						 10 + 2 + frag_len);
 		if (tx_buf) {
 			gas_serv_write_dpp_adv_proto(tx_buf);
+			wpabuf_put_le16(tx_buf, frag_len);
 			wpabuf_put_buf(tx_buf, buf);
 		}
 	} else
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index f9af038..b37f49f 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -439,6 +439,10 @@
 	hostapd_clean_rrm(hapd);
 	fils_hlp_deinit(hapd);
 
+#ifdef CONFIG_OCV
+	eloop_cancel_timeout(hostapd_ocv_check_csa_sa_query, hapd, NULL);
+#endif /* CONFIG_OCV */
+
 #ifdef CONFIG_SAE
 	{
 		struct hostapd_sae_commit_queue *q;
@@ -1911,6 +1915,13 @@
 
 		if (!bss->conf->owe_transition_ifname[0])
 			continue;
+		if (bss->iface->state != HAPD_IFACE_ENABLED) {
+			wpa_printf(MSG_DEBUG,
+				   "OWE: Interface %s state %s - defer beacon update",
+				   bss->conf->iface,
+				   hostapd_state_text(bss->iface->state));
+			continue;
+		}
 		res = hostapd_owe_trans_get_info(bss);
 		if (res == 0)
 			continue;
@@ -3151,6 +3162,7 @@
 
 	hostapd_prune_associations(hapd, sta->addr);
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
+	sta->post_csa_sa_query = 0;
 
 #ifdef CONFIG_P2P
 	if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
@@ -3662,3 +3674,25 @@
 #endif /* CONFIG_NO_RADIUS */
 	}
 }
+
+
+#ifdef CONFIG_OCV
+void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta;
+
+	wpa_printf(MSG_DEBUG, "OCV: Post-CSA SA Query initiation check");
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!sta->post_csa_sa_query)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "OCV: OCVC STA " MACSTR
+			   " did not start SA Query after CSA - disconnect",
+			   MAC2STR(sta->addr));
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+}
+#endif /* CONFIG_OCV */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index a616136..9d3c85d 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -633,6 +633,7 @@
 void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
 void hostapd_periodic_iface(struct hostapd_iface *iface);
 int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
+void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
 
 /* utils.c */
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 565e9af..a872893 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -24,6 +24,7 @@
 #include "common/dpp.h"
 #include "common/ocv.h"
 #include "common/wpa_common.h"
+#include "common/wpa_ctrl.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
@@ -385,7 +386,8 @@
 	    auth_alg == WLAN_AUTH_SAE) {
 		if (auth_transaction == 1 && sta &&
 		    (resp == WLAN_STATUS_SUCCESS ||
-		     resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT)) {
+		     resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+		     resp == WLAN_STATUS_SAE_PK)) {
 			wpa_printf(MSG_DEBUG,
 				   "TESTING: Postpone SAE Commit transmission until Confirm is ready");
 			os_free(sta->sae_postponed_commit);
@@ -477,17 +479,23 @@
 	const char *rx_id = NULL;
 	int use_pt = 0;
 	struct sae_pt *pt = NULL;
+	const struct sae_pk *pk = NULL;
 
 	if (sta->sae->tmp) {
 		rx_id = sta->sae->tmp->pw_id;
-		use_pt = sta->sae->tmp->h2e;
+		use_pt = sta->sae->h2e;
+#ifdef CONFIG_SAE_PK
+		os_memcpy(sta->sae->tmp->own_addr, hapd->own_addr, ETH_ALEN);
+		os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
+#endif /* CONFIG_SAE_PK */
 	}
 
 	if (rx_id && hapd->conf->sae_pwe != 3)
 		use_pt = 1;
 	else if (status_code == WLAN_STATUS_SUCCESS)
 		use_pt = 0;
-	else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+	else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+		 status_code == WLAN_STATUS_SAE_PK)
 		use_pt = 1;
 
 	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
@@ -501,6 +509,8 @@
 			continue;
 		password = pw->password;
 		pt = pw->pt;
+		if (!(hapd->conf->mesh & MESH_ENABLED))
+			pk = pw->pk;
 		break;
 	}
 	if (!password) {
@@ -514,7 +524,7 @@
 
 	if (update && use_pt &&
 	    sae_prepare_commit_pt(sta->sae, pt, hapd->own_addr, sta->addr,
-				  NULL) < 0)
+				  NULL, pk) < 0)
 		return NULL;
 
 	if (update && !use_pt &&
@@ -557,7 +567,17 @@
 	if (buf == NULL)
 		return NULL;
 
-	sae_write_confirm(sta->sae, buf);
+#ifdef CONFIG_SAE_PK
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sta->sae->tmp)
+		sta->sae->tmp->omit_pk_elem = hapd->conf->sae_pk_omit;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_SAE_PK */
+
+	if (sae_write_confirm(sta->sae, buf) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
 
 	return buf;
 }
@@ -577,8 +597,21 @@
 	if (data == NULL)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
-	status = (sta->sae->tmp && sta->sae->tmp->h2e) ?
-		WLAN_STATUS_SAE_HASH_TO_ELEMENT : WLAN_STATUS_SUCCESS;
+	if (sta->sae->tmp && sta->sae->pk)
+		status = WLAN_STATUS_SAE_PK;
+	else if (sta->sae->tmp && sta->sae->h2e)
+		status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
+	else
+		status = WLAN_STATUS_SUCCESS;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->sae_commit_status >= 0 &&
+	    hapd->conf->sae_commit_status != status) {
+		wpa_printf(MSG_INFO,
+			   "TESTING: Override SAE commit status code %u --> %d",
+			   status, hapd->conf->sae_commit_status);
+		status = hapd->conf->sae_commit_status;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
 				    WLAN_AUTH_SAE, 1,
 				    status, wpabuf_head(data),
@@ -899,9 +932,14 @@
 	switch (sta->sae->state) {
 	case SAE_NOTHING:
 		if (auth_transaction == 1) {
-			if (sta->sae->tmp)
-				sta->sae->tmp->h2e = status_code ==
-					WLAN_STATUS_SAE_HASH_TO_ELEMENT;
+			if (sta->sae->tmp) {
+				sta->sae->h2e =
+					(status_code ==
+					 WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+					 status_code == WLAN_STATUS_SAE_PK);
+				sta->sae->pk =
+					status_code == WLAN_STATUS_SAE_PK;
+			}
 			ret = auth_sae_send_commit(hapd, sta, bssid,
 						   !allow_reuse, status_code);
 			if (ret)
@@ -1117,14 +1155,20 @@
 		sae_pwe = 1;
 	else if (id_in_use == 1 && sae_pwe == 0)
 		sae_pwe = 2;
+#ifdef CONFIG_SAE_PK
+	if (sae_pwe == 0 && hostapd_sae_pk_in_use(hapd->conf))
+		sae_pwe = 2;
+#endif /* CONFIG_SAE_PK */
 
 	return ((sae_pwe == 0 || sae_pwe == 3) &&
 		status_code == WLAN_STATUS_SUCCESS) ||
 		(sae_pwe == 1 &&
-		 status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) ||
+		 (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+		  status_code == WLAN_STATUS_SAE_PK)) ||
 		(sae_pwe == 2 &&
 		 (status_code == WLAN_STATUS_SUCCESS ||
-		  status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT));
+		  status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+		  status_code == WLAN_STATUS_SAE_PK));
 }
 
 
@@ -1147,11 +1191,15 @@
 
 
 static int check_sae_rejected_groups(struct hostapd_data *hapd,
-				     const struct wpabuf *groups)
+				     struct sae_data *sae)
 {
+	const struct wpabuf *groups;
 	size_t i, count;
 	const u8 *pos;
 
+	if (!sae->tmp)
+		return 0;
+	groups = sae->tmp->peer_rejected_groups;
 	if (!groups)
 		return 0;
 
@@ -1193,6 +1241,7 @@
 		wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
 		pos = mgmt->u.auth.variable;
 		end = ((const u8 *) mgmt) + len;
+		resp = status_code;
 		send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
 				auth_transaction, resp, pos, end - pos,
 				"auth-sae-reflection-attack");
@@ -1338,7 +1387,8 @@
 					((const u8 *) mgmt) + len -
 					mgmt->u.auth.variable, &token,
 					&token_len, groups, status_code ==
-					WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+					WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+					status_code == WLAN_STATUS_SAE_PK);
 		if (resp == SAE_SILENTLY_DISCARD) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Drop commit message from " MACSTR " due to reflection attack",
@@ -1368,9 +1418,7 @@
 		if (resp != WLAN_STATUS_SUCCESS)
 			goto reply;
 
-		if (sta->sae->tmp &&
-		    check_sae_rejected_groups(
-			    hapd, sta->sae->tmp->peer_rejected_groups)) {
+		if (check_sae_rejected_groups(hapd, sta->sae)) {
 			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto reply;
 		}
@@ -1382,8 +1430,9 @@
 				   "SAE: Request anti-clogging token from "
 				   MACSTR, MAC2STR(sta->addr));
 			if (sta->sae->tmp)
-				h2e = sta->sae->tmp->h2e;
-			if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+				h2e = sta->sae->h2e;
+			if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+			    status_code == WLAN_STATUS_SAE_PK)
 				h2e = 1;
 			data = auth_build_token_req(hapd, sta->sae->group,
 						    sta->addr, h2e);
@@ -3104,6 +3153,34 @@
 #endif /* CONFIG_OWE */
 
 
+static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
+			   int reassoc)
+{
+	if ((sta->flags &
+	     (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
+	    (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
+		return false;
+
+	if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
+		ap_check_sa_query_timeout(hapd, sta);
+
+	if (!sta->sa_query_timed_out &&
+	    (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+		/*
+		 * STA has already been associated with MFP and SA Query timeout
+		 * has not been reached. Reject the association attempt
+		 * temporarily and start SA Query, if one is not pending.
+		 */
+		if (sta->sa_query_count == 0)
+			ap_sta_start_sa_query(hapd, sta);
+
+		return true;
+	}
+
+	return false;
+}
+
+
 static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 			   const u8 *ies, size_t ies_len, int reassoc)
 {
@@ -3182,6 +3259,13 @@
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 		if (is_6ghz_op_class(hapd->iconf->op_class)) {
+			if (!(sta->flags & WLAN_STA_HE)) {
+				hostapd_logger(hapd, sta->addr,
+					       HOSTAPD_MODULE_IEEE80211,
+					       HOSTAPD_LEVEL_INFO,
+					       "Station does not support mandatory HE PHY - reject association");
+				return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+			}
 			resp = copy_sta_he_6ghz_capab(hapd, sta,
 						      elems.he_6ghz_band_cap);
 			if (resp != WLAN_STATUS_SUCCESS)
@@ -3220,6 +3304,8 @@
 	if (hapd->conf->wps_state && elems.wps_ie) {
 		wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
 			   "Request - assume WPS is used");
+		if (check_sa_query(hapd, sta, reassoc))
+			return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
 		sta->flags |= WLAN_STA_WPS;
 		wpabuf_free(sta->wps_ie);
 		sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
@@ -3273,27 +3359,9 @@
 		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
-		if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
-		    (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
-		    !sta->sa_query_timed_out &&
-		    sta->sa_query_count > 0)
-			ap_check_sa_query_timeout(hapd, sta);
-		if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
-		    (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
-		    !sta->sa_query_timed_out &&
-		    (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
-			/*
-			 * STA has already been associated with MFP and SA
-			 * Query timeout has not been reached. Reject the
-			 * association attempt temporarily and start SA Query,
-			 * if one is not pending.
-			 */
 
-			if (sta->sa_query_count == 0)
-				ap_sta_start_sa_query(hapd, sta);
-
+		if (check_sa_query(hapd, sta, reassoc))
 			return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
-		}
 
 		if (wpa_auth_uses_mfp(sta->wpa_sm))
 			sta->flags |= WLAN_STA_MFP;
@@ -3346,7 +3414,7 @@
 
 		if (hapd->conf->sae_pwe == 2 &&
 		    sta->auth_alg == WLAN_AUTH_SAE &&
-		    sta->sae && sta->sae->tmp && !sta->sae->tmp->h2e &&
+		    sta->sae && !sta->sae->h2e &&
 		    elems.rsnxe && elems.rsnxe_len >= 1 &&
 		    (elems.rsnxe[0] & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) {
 			wpa_printf(MSG_INFO, "SAE: " MACSTR
@@ -3494,6 +3562,7 @@
 		struct wpa_channel_info ci;
 		int tx_chanwidth;
 		int tx_seg1_idx;
+		enum oci_verify_result res;
 
 		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
 			wpa_printf(MSG_WARNING,
@@ -3507,9 +3576,20 @@
 					  &tx_seg1_idx) < 0)
 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
-		if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
-					 tx_chanwidth, tx_seg1_idx) != 0) {
-			wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr);
+		res = ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+					   tx_chanwidth, tx_seg1_idx);
+		if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 &&
+		    res == OCI_NOT_FOUND) {
+			/* Work around misbehaving STAs */
+			wpa_printf(MSG_INFO,
+				   "FILS: Disable OCV with a STA that does not send OCI");
+			wpa_auth_set_ocv(sta->wpa_sm, 0);
+		} else if (res != OCI_SUCCESS) {
+			wpa_printf(MSG_WARNING, "FILS: OCV failed: %s",
+				   ocv_errorstr);
+			wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
+				MACSTR " frame=fils-reassoc-req error=%s",
+				MAC2STR(sta->addr), ocv_errorstr);
 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 		}
 	}
@@ -3831,7 +3911,8 @@
 #ifdef CONFIG_OWE
 	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
 	    sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
-	    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
+	    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+	    !wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
 		struct wpabuf *pub;
 
 		pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
@@ -4085,7 +4166,7 @@
 {
 	u16 capab_info, listen_interval, seq_ctrl, fc;
 	int resp = WLAN_STATUS_SUCCESS;
-	u16 reply_res;
+	u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
 	const u8 *pos;
 	int left, i;
 	struct sta_info *sta;
@@ -4465,7 +4546,7 @@
 	os_free(tmp);
 
 	/*
-	 * Remove the station in case tranmission of a success response fails
+	 * Remove the station in case transmission of a success response fails
 	 * (the STA was added associated to the driver) or if the station was
 	 * previously added unassociated.
 	 */
@@ -5511,4 +5592,57 @@
 }
 
 
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 bw, chan1, chan2 = 0;
+	int freq1;
+
+	if (!hapd->cs_freq_params.channel ||
+	    (!hapd->cs_freq_params.vht_enabled &&
+	     !hapd->cs_freq_params.he_enabled))
+		return eid;
+
+	/* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
+	switch (hapd->cs_freq_params.bandwidth) {
+	case 40:
+		bw = 0;
+		break;
+	case 80:
+		/* check if it's 80+80 */
+		if (!hapd->cs_freq_params.center_freq2)
+			bw = 1;
+		else
+			bw = 3;
+		break;
+	case 160:
+		bw = 2;
+		break;
+	default:
+		/* not valid VHT bandwidth or not in CSA */
+		return eid;
+	}
+
+	freq1 = hapd->cs_freq_params.center_freq1 ?
+		hapd->cs_freq_params.center_freq1 :
+		hapd->cs_freq_params.freq;
+	if (ieee80211_freq_to_chan(freq1, &chan1) !=
+	    HOSTAPD_MODE_IEEE80211A)
+		return eid;
+
+	if (hapd->cs_freq_params.center_freq2 &&
+	    ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
+				   &chan2) != HOSTAPD_MODE_IEEE80211A)
+		return eid;
+
+	*eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
+	*eid++ = 5; /* Length of Channel Switch Wrapper */
+	*eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
+	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+	*eid++ = bw; /* New Channel Width */
+	*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
+	*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
+
+	return eid;
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index f1f2442..85b7140 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -314,45 +314,26 @@
 u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid)
 {
 	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	struct he_capabilities *he_cap;
 	struct ieee80211_he_6ghz_band_cap *cap;
-	u32 vht_cap;
-	u8 ht_info;
-	u8 params;
+	u16 capab;
 	u8 *pos;
 
-	if (!mode || !is_6ghz_op_class(hapd->iconf->op_class))
+	if (!mode || !is_6ghz_op_class(hapd->iconf->op_class) ||
+	    !is_6ghz_freq(hapd->iface->freq))
 		return eid;
 
-	vht_cap = hapd->iface->conf->vht_capab;
-	ht_info = mode->a_mpdu_params;
+	he_cap = &mode->he_capab[IEEE80211_MODE_AP];
+	capab = he_cap->he_6ghz_capa;
+	capab |= HE_6GHZ_BAND_CAP_SMPS_DISABLED;
 
 	pos = eid;
 	*pos++ = WLAN_EID_EXTENSION;
 	*pos++ = 1 + sizeof(*cap);
 	*pos++ = WLAN_EID_EXT_HE_6GHZ_BAND_CAP;
 
-	/* Minimum MPDU Start Spacing B0..B2 */
-	params = (ht_info >> 2) & HE_6GHZ_BAND_CAP_MIN_MPDU_START_SPACE_MASK;
-
-	/* Maximum A-MPDU Length Exponent B3..B5 */
-	params |= ((((vht_cap & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >>
-		     VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT) &
-		    HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) <<
-		   HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT);
-
-	/* Maximum MPDU Length B6..B7 */
-	params |= ((((vht_cap & VHT_CAP_MAX_MPDU_LENGTH_MASK) >>
-		     VHT_CAP_MAX_MPDU_LENGTH_MASK_SHIFT) &
-		    HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_MASK) <<
-		   HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_SHIFT);
-
 	cap = (struct ieee80211_he_6ghz_band_cap *) pos;
-	cap->a_mpdu_params = params;
-	cap->info = HE_6GHZ_BAND_CAP_SMPS_DISABLED;
-	if (vht_cap & VHT_CAP_RX_ANTENNA_PATTERN)
-		cap->info |= HE_6GHZ_BAND_CAP_RX_ANTENNA_PATTERN;
-	if (vht_cap & VHT_CAP_TX_ANTENNA_PATTERN)
-		cap->info |= HE_6GHZ_BAND_CAP_TX_ANTENNA_PATTERN;
+	cap->capab = host_to_le16(capab);
 	pos += sizeof(*cap);
 
 	return pos;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 74a837f..17003d5 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ocv.h"
+#include "common/wpa_ctrl.h"
 #include "hostapd.h"
 #include "sta_info.h"
 #include "ap_config.h"
@@ -72,6 +73,16 @@
 				   "Failed to get channel info for OCI element in SA Query Request");
 			return;
 		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (hapd->conf->oci_freq_override_saquery_req) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI frequency %d -> %u MHz",
+				   ci.frequency,
+				   hapd->conf->oci_freq_override_saquery_req);
+			ci.frequency =
+				hapd->conf->oci_freq_override_saquery_req;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 		oci_ie_len = OCV_OCI_EXTENDED_LEN;
 		oci_ie = os_zalloc(oci_ie_len);
@@ -151,6 +162,16 @@
 				   "Failed to get channel info for OCI element in SA Query Response");
 			return;
 		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (hapd->conf->oci_freq_override_saquery_resp) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI frequency %d -> %u MHz",
+				   ci.frequency,
+				   hapd->conf->oci_freq_override_saquery_resp);
+			ci.frequency =
+				hapd->conf->oci_freq_override_saquery_resp;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 		oci_ie_len = OCV_OCI_EXTENDED_LEN;
 		oci_ie = os_zalloc(oci_ie_len);
@@ -254,14 +275,21 @@
 			return;
 
 		if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
-					 tx_chanwidth, tx_seg1_idx) != 0) {
-			wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+					 tx_chanwidth, tx_seg1_idx) !=
+		    OCI_SUCCESS) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
+				MACSTR " frame=saquery%s error=%s",
+				MAC2STR(sa),
+				action_type == WLAN_SA_QUERY_REQUEST ?
+				"req" : "resp", ocv_errorstr);
 			return;
 		}
 	}
 #endif /* CONFIG_OCV */
 
 	if (action_type == WLAN_SA_QUERY_REQUEST) {
+		if (sta)
+			sta->post_csa_sa_query = 0;
 		ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
 		return;
 	}
@@ -400,6 +428,14 @@
 		if (hapd->conf->beacon_prot)
 			*pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
 		break;
+	case 11: /* Bits 88-95 */
+#ifdef CONFIG_SAE_PK
+		if (hapd->conf->wpa &&
+		    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+		    hostapd_sae_pk_exclusively(hapd->conf))
+			*pos |= 0x01; /* Bit 88 - SAE PK Exclusively */
+#endif /* CONFIG_SAE_PK */
+		break;
 	}
 }
 
@@ -460,6 +496,12 @@
 #endif /* CONFIG_SAE */
 	if (len < 11 && hapd->conf->beacon_prot)
 		len = 11;
+#ifdef CONFIG_SAE_PK
+	if (len < 12 && hapd->conf->wpa &&
+	    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+	    hostapd_sae_pk_exclusively(hapd->conf))
+		len = 12;
+#endif /* CONFIG_SAE_PK */
 	if (len < hapd->iface->extended_capa_len)
 		len = hapd->iface->extended_capa_len;
 	if (len == 0)
@@ -1054,11 +1096,16 @@
 u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
 {
 	u8 *pos = eid;
+	bool sae_pk = false;
+
+#ifdef CONFIG_SAE_PK
+	sae_pk = hostapd_sae_pk_in_use(hapd->conf);
+#endif /* CONFIG_SAE_PK */
 
 	if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
 	    !wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) ||
 	    (hapd->conf->sae_pwe != 1 && hapd->conf->sae_pwe != 2 &&
-	     !hostapd_sae_pw_id_in_use(hapd->conf)) ||
+	     !hostapd_sae_pw_id_in_use(hapd->conf) && !sae_pk) ||
 	    hapd->conf->sae_pwe == 3 ||
 	    len < 3)
 		return pos;
@@ -1067,7 +1114,12 @@
 	*pos++ = 1;
 	/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
 	 * used for now */
-	*pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+	*pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+	if (sae_pk)
+		*pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+	pos++;
 
 	return pos;
 }
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index f50f142..c925bf1 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -167,59 +167,6 @@
 }
 
 
-u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
-{
-	u8 bw, chan1, chan2 = 0;
-	int freq1;
-
-	if (!hapd->cs_freq_params.channel ||
-	    !hapd->cs_freq_params.vht_enabled)
-		return eid;
-
-	/* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
-	switch (hapd->cs_freq_params.bandwidth) {
-	case 40:
-		bw = 0;
-		break;
-	case 80:
-		/* check if it's 80+80 */
-		if (!hapd->cs_freq_params.center_freq2)
-			bw = 1;
-		else
-			bw = 3;
-		break;
-	case 160:
-		bw = 2;
-		break;
-	default:
-		/* not valid VHT bandwidth or not in CSA */
-		return eid;
-	}
-
-	freq1 = hapd->cs_freq_params.center_freq1 ?
-		hapd->cs_freq_params.center_freq1 :
-		hapd->cs_freq_params.freq;
-	if (ieee80211_freq_to_chan(freq1, &chan1) !=
-	    HOSTAPD_MODE_IEEE80211A)
-		return eid;
-
-	if (hapd->cs_freq_params.center_freq2 &&
-	    ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
-				   &chan2) != HOSTAPD_MODE_IEEE80211A)
-		return eid;
-
-	*eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
-	*eid++ = 5; /* Length of Channel Switch Wrapper */
-	*eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
-	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
-	*eid++ = bw; /* New Channel Width */
-	*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
-	*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
-
-	return eid;
-}
-
-
 u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
 {
 	struct hostapd_iface *iface = hapd->iface;
@@ -425,7 +372,9 @@
 {
 	u8 *pos = eid;
 
-	if (!hapd->iface->current_mode)
+	/* Vendor VHT is applicable only to 2.4 GHz */
+	if (!hapd->iface->current_mode ||
+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
 		return eid;
 
 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 940d315..ef48561 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -122,6 +122,7 @@
 	unsigned int hs20_t_c_filtering:1;
 	unsigned int ft_over_ds:1;
 	unsigned int external_dh_updated:1;
+	unsigned int post_csa_sa_query:1;
 
 	u16 auth_alg;
 
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 67281b3..be81797 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -103,6 +103,15 @@
 			os_free(wnmtfs_ie);
 			return -1;
 		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (hapd->conf->oci_freq_override_wnm_sleep) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI frequency %d -> %u MHz",
+				   ci.frequency,
+				   hapd->conf->oci_freq_override_wnm_sleep);
+			ci.frequency = hapd->conf->oci_freq_override_wnm_sleep;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 		oci_ie_len = OCV_OCI_EXTENDED_LEN;
 		oci_ie = os_zalloc(oci_ie_len);
@@ -317,8 +326,9 @@
 
 		if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
 					 channel_width_to_int(ci.chanwidth),
-					 ci.seg1_idx) != 0) {
-			wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr);
+					 ci.seg1_idx) != OCI_SUCCESS) {
+			wpa_msg(hapd, MSG_WARNING, "WNM: OCV failed: %s",
+				ocv_errorstr);
 			return;
 		}
 	}
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 019e535..9d74bfc 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -15,6 +15,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ocv.h"
 #include "common/dpp.h"
+#include "common/wpa_ctrl.h"
 #include "crypto/aes.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/aes_siv.h"
@@ -2322,7 +2323,6 @@
 		struct wpa_auth_config *conf = &wpa_auth->conf;
 		u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
 		int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
-		size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
 
 		if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
 				      conf->ssid, conf->ssid_len,
@@ -2333,10 +2333,6 @@
 				      use_sha384) < 0)
 			return -1;
 
-		wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
-				pmk_r0, pmk_r0_len);
-		wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
-			    pmk_r0_name, WPA_PMK_NAME_LEN);
 		wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
 		forced_memzero(fils_ft, sizeof(fils_ft));
 
@@ -2776,6 +2772,15 @@
 			wpabuf_clear_free(plain);
 			return NULL;
 		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (conf->oci_freq_override_fils_assoc) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI frequency %d -> %u MHz",
+				   ci.frequency,
+				   conf->oci_freq_override_fils_assoc);
+			ci.frequency = conf->oci_freq_override_fils_assoc;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 		pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
 		if (ocv_insert_extended_oci(&ci, pos) < 0) {
@@ -3032,6 +3037,7 @@
 		struct wpa_channel_info ci;
 		int tx_chanwidth;
 		int tx_seg1_idx;
+		enum oci_verify_result res;
 
 		if (wpa_channel_info(wpa_auth, &ci) != 0) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -3045,10 +3051,21 @@
 					  &tx_seg1_idx) < 0)
 			return;
 
-		if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
-					 tx_chanwidth, tx_seg1_idx) != 0) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
-					ocv_errorstr);
+		res = ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
+					   tx_chanwidth, tx_seg1_idx);
+		if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
+			/* Work around misbehaving STAs */
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "Disable OCV with a STA that does not send OCI");
+			wpa_auth_set_ocv(sm, 0);
+		} else if (res != OCI_SUCCESS) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "OCV failed: %s", ocv_errorstr);
+			if (wpa_auth->conf.msg_ctx)
+				wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
+					OCV_FAILURE "addr=" MACSTR
+					" frame=eapol-key-m2 error=%s",
+					MAC2STR(sm->addr), ocv_errorstr);
 			return;
 		}
 	}
@@ -3211,6 +3228,14 @@
 	else
 		os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
 	os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random BIGTK to each OSEN STA to prevent use
+		 * of BIGTK in the BSS.
+		 */
+		if (random_get_bytes(bigtk.bigtk, len) < 0)
+			return pos;
+	}
 	pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
 			  (const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
 			  NULL, 0);
@@ -3228,7 +3253,9 @@
 	return 0;
 }
 
-static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos)
+
+static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos,
+		       unsigned int freq)
 {
 #ifdef CONFIG_OCV
 	struct wpa_channel_info ci;
@@ -3241,6 +3268,14 @@
 			   "Failed to get channel info for OCI element");
 		return -1;
 	}
+#ifdef CONFIG_TESTING_OPTIONS
+	if (freq) {
+		wpa_printf(MSG_INFO,
+			   "TEST: Override OCI KDE frequency %d -> %u MHz",
+			   ci.frequency, freq);
+		ci.frequency = freq;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	return ocv_insert_oci_kde(&ci, argpos);
 #else /* CONFIG_OCV */
@@ -3457,7 +3492,7 @@
 				  gtk, gtk_len);
 	}
 	pos = ieee80211w_kde_add(sm, pos);
-	if (ocv_oci_add(sm, &pos) < 0)
+	if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0)
 		goto done;
 
 #ifdef CONFIG_IEEE80211R_AP
@@ -3807,7 +3842,8 @@
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 				  gtk, gsm->GTK_len);
 		pos = ieee80211w_kde_add(sm, pos);
-		if (ocv_oci_add(sm, &pos) < 0) {
+		if (ocv_oci_add(sm, &pos,
+				conf->oci_freq_override_eapol_g1) < 0) {
 			os_free(kde_buf);
 			return;
 		}
@@ -3873,7 +3909,7 @@
 
 		if (wpa_channel_info(wpa_auth, &ci) != 0) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
-					"Failed to get channel info to validate received OCI in EAPOL-Key group 1/2");
+					"Failed to get channel info to validate received OCI in EAPOL-Key group 2/2");
 			return;
 		}
 
@@ -3884,9 +3920,15 @@
 			return;
 
 		if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
-					 tx_chanwidth, tx_seg1_idx) != 0) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
-					ocv_errorstr);
+					 tx_chanwidth, tx_seg1_idx) !=
+		    OCI_SUCCESS) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+					 "OCV failed: %s", ocv_errorstr);
+			if (wpa_auth->conf.msg_ctx)
+				wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
+					OCV_FAILURE "addr=" MACSTR
+					" frame=eapol-key-g2 error=%s",
+					MAC2STR(sm->addr), ocv_errorstr);
 			return;
 		}
 	}
@@ -4072,6 +4114,7 @@
 
 int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
 	struct wpa_group *gsm = sm->group;
 	u8 *start = pos;
 
@@ -4090,6 +4133,14 @@
 		return 0;
 	pos += 8;
 	os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use
+		 * of GTK in the BSS.
+		 */
+		if (random_get_bytes(pos, gsm->GTK_len) < 0)
+			return 0;
+	}
 	pos += gsm->GTK_len;
 
 	wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
@@ -4103,6 +4154,7 @@
 
 int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
 	struct wpa_group *gsm = sm->group;
 	u8 *start = pos;
 	size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
@@ -4120,6 +4172,14 @@
 	pos += 6;
 
 	os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
+	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random IGTK to each STA to prevent use
+		 * of IGTK in the BSS.
+		 */
+		if (random_get_bytes(pos, len) < 0)
+			return 0;
+	}
 	pos += len;
 
 	wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
@@ -4150,6 +4210,14 @@
 	pos += 6;
 
 	os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random BIGTK to each STA to prevent use
+		 * of BIGTK in the BSS.
+		 */
+		if (random_get_bytes(pos, len) < 0)
+			return 0;
+	}
 	pos += len;
 
 	wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
@@ -5228,6 +5296,14 @@
 #endif /* CONFIG_DPP2 */
 
 
+void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
+				     u8 val)
+{
+	if (wpa_auth)
+		wpa_auth->conf.transition_disable = val;
+}
+
+
 #ifdef CONFIG_TESTING_OPTIONS
 
 int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
@@ -5259,6 +5335,7 @@
 	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
 	u8 *opos;
 	size_t gtk_len, kde_len;
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
 	struct wpa_group *gsm = sm->group;
 	u8 *wpa_ie;
 	int wpa_ie_len, secure, gtkidx, encr = 0;
@@ -5369,7 +5446,7 @@
 		opos += 2 + RSN_SELECTOR_LEN + 2;
 		os_memset(opos, 0, 6); /* clear PN */
 	}
-	if (ocv_oci_add(sm, &pos) < 0) {
+	if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0) {
 		os_free(kde);
 		return -1;
 	}
@@ -5377,9 +5454,7 @@
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		int res;
-		struct wpa_auth_config *conf;
 
-		conf = &sm->wpa_auth->conf;
 		if (sm->assoc_resp_ftie &&
 		    kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
 			os_memcpy(pos, sm->assoc_resp_ftie,
@@ -5436,6 +5511,7 @@
 			     void *ctx1, void *ctx2)
 {
 	u8 rsc[WPA_KEY_RSC_LEN];
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
 	struct wpa_group *gsm = sm->group;
 	const u8 *kde;
 	u8 *kde_buf = NULL, *pos, hdr[2];
@@ -5470,7 +5546,8 @@
 			opos += 2 + RSN_SELECTOR_LEN + 2;
 			os_memset(opos, 0, 6); /* clear PN */
 		}
-		if (ocv_oci_add(sm, &pos) < 0) {
+		if (ocv_oci_add(sm, &pos,
+				conf->oci_freq_override_eapol_g1) < 0) {
 			os_free(kde_buf);
 			return -1;
 		}
@@ -5512,4 +5589,27 @@
 		wpa_auth->conf.ft_rsnxe_used = val;
 }
 
+
+void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth,
+				    enum wpa_auth_ocv_override_frame frame,
+				    unsigned int freq)
+{
+	if (!wpa_auth)
+		return;
+	switch (frame) {
+	case WPA_AUTH_OCV_OVERRIDE_EAPOL_M3:
+		wpa_auth->conf.oci_freq_override_eapol_m3 = freq;
+		break;
+	case WPA_AUTH_OCV_OVERRIDE_EAPOL_G1:
+		wpa_auth->conf.oci_freq_override_eapol_g1 = freq;
+		break;
+	case WPA_AUTH_OCV_OVERRIDE_FT_ASSOC:
+		wpa_auth->conf.oci_freq_override_ft_assoc = freq;
+		break;
+	case WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC:
+		wpa_auth->conf.oci_freq_override_fils_assoc = freq;
+		break;
+	}
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 1ea067b..5f9df9c 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -168,6 +168,7 @@
 
 
 struct wpa_auth_config {
+	void *msg_ctx;
 	int wpa;
 	int extended_key_id;
 	int wpa_key_mgmt;
@@ -240,6 +241,10 @@
 	unsigned int igtk_rsc_override_set:1;
 	int ft_rsnxe_used;
 #endif /* CONFIG_TESTING_OPTIONS */
+	unsigned int oci_freq_override_eapol_m3;
+	unsigned int oci_freq_override_eapol_g1;
+	unsigned int oci_freq_override_ft_assoc;
+	unsigned int oci_freq_override_fils_assoc;
 #ifdef CONFIG_P2P
 	u8 ip_addr_go[4];
 	u8 ip_addr_mask[4];
@@ -251,6 +256,7 @@
 	u8 fils_cache_id[FILS_CACHE_ID_LEN];
 #endif /* CONFIG_FILS */
 	int sae_pwe;
+	bool sae_pk;
 	int owe_ptk_workaround;
 	u8 transition_disable;
 #ifdef CONFIG_DPP2
@@ -511,6 +517,8 @@
 				    const u8 *req_ies, size_t req_ies_len);
 void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
 void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
+void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
+				     u8 val);
 
 int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
 		       void (*cb)(void *ctx1, void *ctx2),
@@ -525,4 +533,14 @@
 void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm);
 void wpa_auth_set_ft_rsnxe_used(struct wpa_authenticator *wpa_auth, int val);
 
+enum wpa_auth_ocv_override_frame {
+	WPA_AUTH_OCV_OVERRIDE_EAPOL_M3,
+	WPA_AUTH_OCV_OVERRIDE_EAPOL_G1,
+	WPA_AUTH_OCV_OVERRIDE_FT_ASSOC,
+	WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC,
+};
+void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth,
+				    enum wpa_auth_ocv_override_frame frame,
+				    unsigned int freq);
+
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 5af65aa..5aa363e 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -14,6 +14,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/ocv.h"
+#include "common/wpa_ctrl.h"
 #include "drivers/driver.h"
 #include "crypto/aes.h"
 #include "crypto/aes_siv.h"
@@ -1897,7 +1898,7 @@
 		return;
 	}
 
-	wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID",
+	wpa_hexdump(MSG_DEBUG, "FT: Temporarily block R0KH-ID",
 		    f_r0kh_id, f_r0kh_id_len);
 
 	if (r0kh) {
@@ -1985,7 +1986,7 @@
 		return -1;
 	}
 	if (is_zero_ether_addr(r0kh->addr)) {
-		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted",
+		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is temporarily blocked",
 			    sm->r0kh_id, sm->r0kh_id_len);
 		return -1;
 	}
@@ -2128,8 +2129,6 @@
 			      pmk_r0, pmk_r0_name,
 			      wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
 		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
 	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
 		wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
 				    pmk_r0_name,
@@ -2140,9 +2139,6 @@
 	if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
 			      pmk_r1, sm->pmk_r1_name) < 0)
 		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
-		    WPA_PMK_NAME_LEN);
 	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
 		wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len,
 				    sm->pmk_r1_name, sm->pairwise, &vlan,
@@ -2167,11 +2163,12 @@
 static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
 {
 	u8 *subelem;
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
 	struct wpa_group *gsm = sm->group;
 	size_t subelem_len, pad_len;
 	const u8 *key;
 	size_t key_len;
-	u8 keybuf[32];
+	u8 keybuf[WPA_GTK_MAX_LEN];
 	const u8 *kek;
 	size_t kek_len;
 
@@ -2198,12 +2195,30 @@
 		pad_len += 8;
 	if (pad_len && key_len < sizeof(keybuf)) {
 		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
+		if (conf->disable_gtk ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+			/*
+			 * Provide unique random GTK to each STA to prevent use
+			 * of GTK in the BSS.
+			 */
+			if (random_get_bytes(keybuf, key_len) < 0)
+				return NULL;
+		}
 		os_memset(keybuf + key_len, 0, pad_len);
 		keybuf[key_len] = 0xdd;
 		key_len += pad_len;
 		key = keybuf;
-	} else
+	} else if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use of GTK
+		 * in the BSS.
+		 */
+		if (random_get_bytes(keybuf, key_len) < 0)
+			return NULL;
+		key = keybuf;
+	} else {
 		key = gsm->GTK[gsm->GN - 1];
+	}
 
 	/*
 	 * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
@@ -2237,11 +2252,13 @@
 static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
 {
 	u8 *subelem, *pos;
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
 	struct wpa_group *gsm = sm->group;
 	size_t subelem_len;
-	const u8 *kek;
+	const u8 *kek, *igtk;
 	size_t kek_len;
 	size_t igtk_len;
+	u8 dummy_igtk[WPA_IGTK_MAX_LEN];
 
 	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
 		kek = sm->PTK.kek2;
@@ -2268,8 +2285,19 @@
 	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
 	pos += 6;
 	*pos++ = igtk_len;
-	if (aes_wrap(kek, kek_len, igtk_len / 8,
-		     gsm->IGTK[gsm->GN_igtk - 4], pos)) {
+	igtk = gsm->IGTK[gsm->GN_igtk - 4];
+	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random IGTK to each STA to prevent use of
+		 * IGTK in the BSS.
+		 */
+		if (random_get_bytes(dummy_igtk, igtk_len / 8) < 0) {
+			os_free(subelem);
+			return NULL;
+		}
+		igtk = dummy_igtk;
+	}
+	if (aes_wrap(kek, kek_len, igtk_len / 8, igtk, pos)) {
 		wpa_printf(MSG_DEBUG,
 			   "FT: IGTK subelem encryption failed: kek_len=%d",
 			   (int) kek_len);
@@ -2287,9 +2315,10 @@
 	u8 *subelem, *pos;
 	struct wpa_group *gsm = sm->group;
 	size_t subelem_len;
-	const u8 *kek;
+	const u8 *kek, *bigtk;
 	size_t kek_len;
 	size_t bigtk_len;
+	u8 dummy_bigtk[WPA_IGTK_MAX_LEN];
 
 	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
 		kek = sm->PTK.kek2;
@@ -2316,8 +2345,19 @@
 	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos);
 	pos += 6;
 	*pos++ = bigtk_len;
-	if (aes_wrap(kek, kek_len, bigtk_len / 8,
-		     gsm->IGTK[gsm->GN_bigtk - 6], pos)) {
+	bigtk = gsm->IGTK[gsm->GN_bigtk - 6];
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random BIGTK to each OSEN STA to prevent use
+		 * of BIGTK in the BSS.
+		 */
+		if (random_get_bytes(dummy_bigtk, bigtk_len / 8) < 0) {
+			os_free(subelem);
+			return NULL;
+		}
+		bigtk = dummy_bigtk;
+	}
+	if (aes_wrap(kek, kek_len, bigtk_len / 8, bigtk, pos)) {
 		wpa_printf(MSG_DEBUG,
 			   "FT: BIGTK subelem encryption failed: kek_len=%d",
 			   (int) kek_len);
@@ -2621,6 +2661,15 @@
 				os_free(subelem);
 				return NULL;
 			}
+#ifdef CONFIG_TESTING_OPTIONS
+			if (conf->oci_freq_override_ft_assoc) {
+				wpa_printf(MSG_INFO,
+					   "TEST: Override OCI frequency %d -> %u MHz",
+					   ci.frequency,
+					   conf->oci_freq_override_ft_assoc);
+				ci.frequency = conf->oci_freq_override_ft_assoc;
+			}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 			subelem_len += 2 + OCV_OCI_LEN;
 			nbuf = os_realloc(subelem, subelem_len);
@@ -2961,8 +3010,6 @@
 			      conf->r1_key_holder,
 			      sm->addr, out_pmk_r1, pmk_r1_name) < 0)
 		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
 
 	os_get_reltime(&now);
 	if (r0->expiration)
@@ -3091,8 +3138,6 @@
 				   sm->wpa_auth->conf.r1_key_holder, sm->addr,
 				   pmk_r1_name, use_sha384) < 0)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
-		    pmk_r1_name, WPA_PMK_NAME_LEN);
 
 	if (conf->ft_psk_generate_local &&
 	    wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
@@ -3460,6 +3505,7 @@
 		struct wpa_channel_info ci;
 		int tx_chanwidth;
 		int tx_seg1_idx;
+		enum oci_verify_result res;
 
 		if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
 			wpa_printf(MSG_WARNING,
@@ -3473,10 +3519,21 @@
 					  &tx_seg1_idx) < 0)
 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
-		if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
-					 tx_chanwidth, tx_seg1_idx) != 0) {
-			wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		res = ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
+					   tx_chanwidth, tx_seg1_idx);
+		if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
+			/* Work around misbehaving STAs */
+			wpa_printf(MSG_INFO,
+				   "Disable OCV with a STA that does not send OCI");
+			wpa_auth_set_ocv(sm, 0);
+		} else if (res != OCI_SUCCESS) {
+			wpa_printf(MSG_WARNING, "OCV failed: %s", ocv_errorstr);
+			if (sm->wpa_auth->conf.msg_ctx)
+				wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+					OCV_FAILURE "addr=" MACSTR
+					" frame=ft-reassoc-req error=%s",
+					MAC2STR(sm->addr), ocv_errorstr);
+			return WLAN_STATUS_INVALID_FTIE;
 		}
 	}
 #endif /* CONFIG_OCV */
@@ -3699,14 +3756,11 @@
 		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
 	};
 
+	wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 for peer AP");
 	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
 			      pmk_r0->pmk_r0_name, r1kh_id,
 			      s1kh_id, pmk_r1, pmk_r1_name) < 0)
 		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)",
-			pmk_r1, pmk_r1_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)",
-		    pmk_r1_name, WPA_PMK_NAME_LEN);
 	WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
 
 	os_get_reltime(&now);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 05d87ac..c01654f 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -175,6 +175,11 @@
 		wconf->igtk_rsc_override_set = 1;
 	}
 	wconf->ft_rsnxe_used = conf->ft_rsnxe_used;
+	wconf->oci_freq_override_eapol_m3 = conf->oci_freq_override_eapol_m3;
+	wconf->oci_freq_override_eapol_g1 = conf->oci_freq_override_eapol_g1;
+	wconf->oci_freq_override_ft_assoc = conf->oci_freq_override_ft_assoc;
+	wconf->oci_freq_override_fils_assoc =
+		conf->oci_freq_override_fils_assoc;
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_P2P
 	os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
@@ -193,6 +198,9 @@
 		wconf->sae_pwe = 1;
 	else if (sae_pw_id == 1 && wconf->sae_pwe == 0)
 		wconf->sae_pwe = 2;
+#ifdef CONFIG_SAE_PK
+	wconf->sae_pk = hostapd_sae_pk_in_use(conf);
+#endif /* CONFIG_SAE_PK */
 #ifdef CONFIG_OWE
 	wconf->owe_ptk_workaround = conf->owe_ptk_workaround;
 #endif /* CONFIG_OWE */
@@ -1451,6 +1459,7 @@
 	size_t wpa_ie_len;
 
 	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
+	_conf.msg_ctx = hapd->msg_ctx;
 	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
 		_conf.tx_status = 1;
 	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index ba08ac2..a6dc1a5 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -95,8 +95,9 @@
 #endif /* CONFIG_IEEE80211R_AP */
 	unsigned int is_wnmsleep:1;
 	unsigned int pmkid_set:1;
+
 #ifdef CONFIG_OCV
-	unsigned int ocv_enabled:1;
+	int ocv_enabled;
 #endif /* CONFIG_OCV */
 
 	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 8dfd657..3704fc0 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -378,7 +378,7 @@
 {
 	u8 *pos = buf;
 
-	if (conf->sae_pwe != 1 && conf->sae_pwe != 2)
+	if (conf->sae_pwe != 1 && conf->sae_pwe != 2 && !conf->sae_pk)
 		return 0; /* no supported extended RSN capabilities */
 
 	if (len < 3)
@@ -388,7 +388,12 @@
 	*pos++ = 1;
 	/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
 	 * used for now */
-	*pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+	*pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+	if (conf->sae_pk)
+		*pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+	pos++;
 
 	return pos - buf;
 }
@@ -803,14 +808,26 @@
 #endif /* CONFIG_SAE */
 
 #ifdef CONFIG_OCV
-	if ((data.capabilities & WPA_CAPABILITY_OCVC) &&
+	if (wpa_auth->conf.ocv && (data.capabilities & WPA_CAPABILITY_OCVC) &&
 	    !(data.capabilities & WPA_CAPABILITY_MFPC)) {
-		wpa_printf(MSG_DEBUG,
-			   "Management frame protection required with OCV, but client did not enable it");
-		return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+		/* Some legacy MFP incapable STAs wrongly copy OCVC bit from
+		 * AP RSN capabilities. To improve interoperability with such
+		 * legacy STAs allow connection without enabling OCV when the
+		 * workaround mode (ocv=2) is enabled.
+		 */
+		if (wpa_auth->conf.ocv == 2) {
+			wpa_printf(MSG_DEBUG,
+				   "Allow connecting MFP incapable and OCV capable STA without enabling OCV");
+			wpa_auth_set_ocv(sm, 0);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "Management frame protection required with OCV, but client did not enable it");
+			return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+		}
+	} else {
+		wpa_auth_set_ocv(sm, (data.capabilities & WPA_CAPABILITY_OCVC) ?
+				 wpa_auth->conf.ocv : 0);
 	}
-	wpa_auth_set_ocv(sm, wpa_auth->conf.ocv &&
-			 (data.capabilities & WPA_CAPABILITY_OCVC));
 #endif /* CONFIG_OCV */
 
 	if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
diff --git a/src/build.rules b/src/build.rules
new file mode 100644
index 0000000..acda884
--- /dev/null
+++ b/src/build.rules
@@ -0,0 +1,109 @@
+.PHONY: all
+all: _all
+
+# disable built-in rules
+.SUFFIXES:
+
+# setup some variables
+ROOTDIR := $(dir $(lastword $(MAKEFILE_LIST)))
+ROOTDIR := $(dir $(ROOTDIR:%../src/=%))../
+BUILDDIR ?= $(abspath $(ROOTDIR)build)
+BUILDDIR := $(BUILDDIR:%/=%)
+ABSROOT := $(abspath $(ROOTDIR))
+ifeq ($(origin OUT),command line)
+_PROJ := $(OUT:%/=%)
+_PROJ := $(_PROJ:$(BUILDDIR)/%=%)
+else
+_PROJ := $(abspath $(dir $(firstword $(MAKEFILE_LIST))))
+_PROJ := $(_PROJ:$(ABSROOT)/%=%)
+endif
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef RANLIB
+RANLIB=ranlib
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+ifneq ($(CONFIG_FILE),)
+-include $(CONFIG_FILE)
+
+# export for sub-makefiles
+export CONFIG_CODE_COVERAGE
+
+.PHONY: verify_config
+verify_config:
+	@if [ ! -r $(CONFIG_FILE) ]; then \
+		echo 'Building $(firstword $(ALL)) requires a configuration file'; \
+		echo '(.config). See README for more instructions. You can'; \
+		echo 'run "cp defconfig .config" to create an example'; \
+		echo 'configuration.'; \
+		exit 1; \
+	fi
+VERIFY := verify_config
+else
+VERIFY :=
+endif
+
+# default target
+.PHONY: _all
+_all: $(VERIFY) $(ALL) $(EXTRA_TARGETS)
+
+# continue setup
+COVSUFFIX := $(if $(CONFIG_CODE_COVERAGE),-cov,)
+PROJ := $(_PROJ)$(COVSUFFIX)
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
+
+ifeq ($(Q),@)
+MAKEFLAGS += --no-print-directory
+endif
+
+_DIRS := $(BUILDDIR)/$(PROJ)
+.PHONY: _make_dirs
+_make_dirs:
+	@mkdir -p $(_DIRS)
+
+$(BUILDDIR)/$(PROJ)/src/%.o: $(ROOTDIR)src/%.c $(CONFIG_FILE) | _make_dirs
+	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
+	@$(E) "  CC " $<
+$(BUILDDIR)/$(PROJ)/%.o: %.c $(CONFIG_FILE) | _make_dirs
+	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
+	@$(E) "  CC " $<
+# for the fuzzing tests
+$(BUILDDIR)/$(PROJ)/wpa_supplicant/%.o: $(ROOTDIR)wpa_supplicant/%.c $(CONFIG_FILE) | _make_dirs
+	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
+	@$(E) "  CC " $<
+
+# libraries - they know how to build themselves
+# (lib_phony so we recurse all the time)
+.PHONY: lib_phony
+lib_phony:
+# nothing
+
+$(BUILDDIR)/$(PROJ)/%.a: $(CONFIG_FILE) lib_phony
+	$(Q)$(MAKE) -C $(ROOTDIR)$(dir $(@:$(BUILDDIR)/$(PROJ)/%=%)) OUT=$(abspath $(dir $@))/
+
+BUILDOBJ = $(patsubst %,$(BUILDDIR)/$(PROJ)/%,$(patsubst $(ROOTDIR)%,%,$(1)))
+
+.PHONY: common-clean
+common-clean:
+	$(Q)rm -rf $(ALL) $(BUILDDIR)/$(PROJ)
diff --git a/src/common/Makefile b/src/common/Makefile
index ccb280e..59ba6c5 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -1,13 +1,3 @@
-all: libcommon.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcommon.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 CFLAGS += -DCONFIG_IEEE80211R
 CFLAGS += -DCONFIG_HS20
 CFLAGS += -DCONFIG_SAE
@@ -21,7 +11,4 @@
 	sae.o \
 	wpa_common.o
 
-libcommon.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/common/brcm_vendor.h b/src/common/brcm_vendor.h
index cb5b348..b1bf397 100644
--- a/src/common/brcm_vendor.h
+++ b/src/common/brcm_vendor.h
@@ -27,6 +27,7 @@
  */
 enum brcm_nl80211_vendor_subcmds {
 	BRCM_VENDOR_SUBCMD_UNSPEC		= 0,
+	BRCM_VENDOR_SUBCMD_SET_MAC		= 6,
 	BRCM_VENDOR_SCMD_ACS			= 9,
 	BRCM_VENDOR_SCMD_MAX			= 10
 };
@@ -61,5 +62,12 @@
 
 	BRCM_VENDOR_ATTR_ACS_LAST
 };
+
+enum brcm_wlan_vendor_attr {
+	BRCM_ATTR_DRIVER_CMD            = 0,
+	BRCM_ATTR_DRIVER_MAC_ADDR	= 3,
+	BRCM_ATTR_DRIVER_AFTER_LAST     = 5,
+	BRCM_ATTR_DRIVER_MAX            = BRCM_ATTR_DRIVER_AFTER_LAST - 1,
+};
 #endif /* BRCM_VENDOR_H */
 
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index a58bf66..00308d4 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -548,6 +548,95 @@
 }
 
 
+static int sae_pk_tests(void)
+{
+#ifdef CONFIG_SAE_PK
+	const char *invalid[] = { "a2bc-de3f-ghim-", "a2bcde3fghim", "", NULL };
+	struct {
+		const char *pw;
+		const u8 *val;
+	} valid[] = {
+		{ "a2bc-de3f-ghim", (u8 *) "\x06\x82\x21\x93\x65\x31\xd0\xc0" },
+		{ "aaaa-aaaa-aaaj", (u8 *) "\x00\x00\x00\x00\x00\x00\x00\x90" },
+		{ "7777-7777-777f", (u8 *) "\xff\xff\xff\xff\xff\xff\xfe\x50" },
+		{ NULL, NULL }
+	};
+	int i;
+	bool failed;
+
+	for (i = 0; invalid[i]; i++) {
+		if (sae_pk_valid_password(invalid[i])) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Invalid password '%s' not recognized",
+				   invalid[i]);
+			return -1;
+		}
+	}
+
+	failed = false;
+	for (i = 0; valid[i].pw; i++) {
+		u8 *res;
+		size_t res_len;
+		char *b32;
+		const char *pw = valid[i].pw;
+		const u8 *val = valid[i].val;
+		size_t pw_len = os_strlen(pw);
+		size_t bits = (pw_len - pw_len / 5) * 5;
+		size_t bytes = (bits + 7) / 8;
+
+		if (!sae_pk_valid_password(pw)) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Valid password '%s' not recognized",
+				   pw);
+			failed = true;
+			continue;
+		}
+
+		res = sae_pk_base32_decode(pw, pw_len, &res_len);
+		if (!res) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Failed to decode password '%s'",
+				   valid[i].pw);
+			failed = true;
+			continue;
+		}
+		if (res_len != bytes || os_memcmp(val, res, res_len) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Mismatch for decoded password '%s'",
+				   valid[i].pw);
+			wpa_hexdump(MSG_INFO, "SAE-PK: Decoded value",
+				    res, res_len);
+			wpa_hexdump(MSG_INFO, "SAE-PK: Expected value",
+				    val, bytes);
+			failed = true;
+		}
+		os_free(res);
+
+		b32 = sae_pk_base32_encode(val, bits - 5);
+		if (!b32) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Failed to encode password '%s'",
+				   pw);
+			failed = true;
+			continue;
+		}
+		if (os_strcmp(b32, pw) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "SAE-PK: Mismatch for password '%s'", pw);
+			wpa_printf(MSG_INFO, "SAE-PK: Encoded value: '%s'",
+				   b32);
+			failed = true;
+		}
+		os_free(b32);
+	}
+
+	return failed ? -1 : 0;
+#else /* CONFIG_SAE_PK */
+	return 0;
+#endif /* CONFIG_SAE_PK */
+}
+
+
 int common_module_tests(void)
 {
 	int ret = 0;
@@ -557,6 +646,7 @@
 	if (ieee802_11_parse_tests() < 0 ||
 	    gas_tests() < 0 ||
 	    sae_tests() < 0 ||
+	    sae_pk_tests() < 0 ||
 	    rsn_ie_parse_tests() < 0)
 		ret = -1;
 
diff --git a/src/common/dpp.c b/src/common/dpp.c
index d09f0d1..67b8f07 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -17,6 +17,7 @@
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "common/gas.h"
+#include "eap_common/eap_defs.h"
 #include "crypto/crypto.h"
 #include "crypto/random.h"
 #include "crypto/aes.h"
@@ -244,6 +245,7 @@
 		wpa_printf(MSG_DEBUG,
 			   "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
 			   opclass, channel, freq);
+		bi->channels_listed = true;
 		if (freq < 0) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
@@ -828,6 +830,7 @@
 	const char *tech = "infra";
 	const char *dpp_name;
 	struct wpabuf *buf, *json;
+	char *csr = NULL;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) {
@@ -844,6 +847,17 @@
 	len = 100 + name_len * 6 + 1 + int_array_len(opclasses) * 4;
 	if (mud_url && mud_url[0])
 		len += 10 + os_strlen(mud_url);
+#ifdef CONFIG_DPP2
+	if (auth->csr) {
+		size_t csr_len;
+
+		csr = base64_encode_no_lf(wpabuf_head(auth->csr),
+					  wpabuf_len(auth->csr), &csr_len);
+		if (!csr)
+			return NULL;
+		len += 30 + csr_len;
+	}
+#endif /* CONFIG_DPP2 */
 	json = wpabuf_alloc(len);
 	if (!json)
 		return NULL;
@@ -870,10 +884,15 @@
 			wpabuf_printf(json, "%s%u", i ? "," : "", opclasses[i]);
 		json_end_array(json);
 	}
+	if (csr) {
+		json_value_sep(json);
+		json_add_string(json, "pkcs10", csr);
+	}
 	json_end_object(json);
 
 	buf = dpp_build_conf_req(auth, wpabuf_head(json));
 	wpabuf_free(json);
+	os_free(csr);
 
 	return buf;
 }
@@ -916,6 +935,8 @@
 		conf->akm = DPP_AKM_PSK_SAE_DPP;
 	else if (bin_str_eq(type, len, "dpp"))
 		conf->akm = DPP_AKM_DPP;
+	else if (bin_str_eq(type, len, "dot1x"))
+		conf->akm = DPP_AKM_DOT1X;
 	else
 		goto fail;
 
@@ -978,6 +999,7 @@
 		return;
 	str_clear_free(conf->passphrase);
 	os_free(conf->group_id);
+	os_free(conf->csrattrs);
 	bin_clear_free(conf, sizeof(*conf));
 }
 
@@ -988,6 +1010,7 @@
 	const char *pos, *end;
 	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
 	struct dpp_configuration *conf = NULL;
+	size_t len;
 
 	pos = os_strstr(cmd, " conf=sta-");
 	if (pos) {
@@ -1092,6 +1115,17 @@
 		conf->netaccesskey_expiry = val;
 	}
 
+	pos = os_strstr(cmd, " csrattrs=");
+	if (pos) {
+		pos += 10;
+		end = os_strchr(pos, ' ');
+		len = end ? (size_t) (end - pos) : os_strlen(pos);
+		conf->csrattrs = os_zalloc(len + 1);
+		if (!conf->csrattrs)
+			goto fail;
+		os_memcpy(conf->csrattrs, pos, len);
+	}
+
 	if (!dpp_configuration_valid(conf))
 		goto fail;
 
@@ -1247,12 +1281,28 @@
 
 		os_free(conf->connector);
 		wpabuf_free(conf->c_sign_key);
+		wpabuf_free(conf->certbag);
+		wpabuf_free(conf->certs);
+		wpabuf_free(conf->cacert);
+		os_free(conf->server_name);
+		wpabuf_free(conf->pp_key);
 	}
 #ifdef CONFIG_DPP2
 	dpp_free_asymmetric_key(auth->conf_key_pkg);
+	os_free(auth->csrattrs);
+	wpabuf_free(auth->csr);
+	wpabuf_free(auth->priv_key);
+	wpabuf_free(auth->cacert);
+	wpabuf_free(auth->certbag);
+	os_free(auth->trusted_eap_server_name);
+	wpabuf_free(auth->conf_resp_tcp);
 #endif /* CONFIG_DPP2 */
 	wpabuf_free(auth->net_access_key);
 	dpp_bootstrap_info_free(auth->tmp_own_bi);
+	if (auth->tmp_peer_bi) {
+		dl_list_del(&auth->tmp_peer_bi->list);
+		dpp_bootstrap_info_free(auth->tmp_peer_bi);
+	}
 #ifdef CONFIG_TESTING_OPTIONS
 	os_free(auth->config_obj_override);
 	os_free(auth->discovery_override);
@@ -1480,6 +1530,15 @@
 	tailroom += os_strlen(signed_conn);
 	if (incl_legacy)
 		tailroom += 1000;
+	if (akm == DPP_AKM_DOT1X) {
+		if (auth->certbag)
+			tailroom += 2 * wpabuf_len(auth->certbag);
+		if (auth->cacert)
+			tailroom += 2 * wpabuf_len(auth->cacert);
+		if (auth->trusted_eap_server_name)
+			tailroom += os_strlen(auth->trusted_eap_server_name);
+		tailroom += 1000;
+	}
 	buf = dpp_build_conf_start(auth, conf, tailroom);
 	if (!buf)
 		goto fail;
@@ -1495,6 +1554,30 @@
 		dpp_build_legacy_cred_params(buf, conf);
 		json_value_sep(buf);
 	}
+	if (akm == DPP_AKM_DOT1X) {
+		json_start_object(buf, "entCreds");
+		if (!auth->certbag)
+			goto fail;
+		json_add_base64(buf, "certBag", wpabuf_head(auth->certbag),
+				wpabuf_len(auth->certbag));
+		if (auth->cacert) {
+			json_value_sep(buf);
+			json_add_base64(buf, "caCert",
+					wpabuf_head(auth->cacert),
+					wpabuf_len(auth->cacert));
+		}
+		if (auth->trusted_eap_server_name) {
+			json_value_sep(buf);
+			json_add_string(buf, "trustedEapServerName",
+					auth->trusted_eap_server_name);
+		}
+		json_value_sep(buf);
+		json_start_array(buf, "eapMethods");
+		wpabuf_printf(buf, "%d", EAP_TYPE_TLS);
+		json_end_array(buf);
+		json_end_object(buf);
+		json_value_sep(buf);
+	}
 	wpabuf_put_str(buf, "\"signedConnector\":\"");
 	wpabuf_put_str(buf, signed_conn);
 	wpabuf_put_str(buf, "\"");
@@ -1504,6 +1587,16 @@
 		wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
 		goto fail;
 	}
+#ifdef CONFIG_DPP2
+	if (auth->peer_version >= 2 && auth->conf->pp_key) {
+		json_value_sep(buf);
+		if (dpp_build_jwk(buf, "ppKey", auth->conf->pp_key, NULL,
+				  curve) < 0) {
+			wpa_printf(MSG_DEBUG, "DPP: Failed to build ppKey JWK");
+			goto fail;
+		}
+	}
+#endif /* CONFIG_DPP2 */
 
 	json_end_object(buf);
 	json_end_object(buf);
@@ -1554,7 +1647,7 @@
 
 static struct wpabuf *
 dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole,
-		   int idx)
+		   int idx, bool cert_req)
 {
 	struct dpp_configuration *conf = NULL;
 
@@ -1587,15 +1680,28 @@
 		return NULL;
 	}
 
+	if (conf->akm == DPP_AKM_DOT1X) {
+		if (!auth->conf) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: No Configurator data available");
+			return NULL;
+		}
+		if (!cert_req && !auth->certbag) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: No certificate data available for dot1x configuration");
+			return NULL;
+		}
+		return dpp_build_conf_obj_dpp(auth, conf);
+	}
 	if (dpp_akm_dpp(conf->akm) || (auth->peer_version >= 2 && auth->conf))
 		return dpp_build_conf_obj_dpp(auth, conf);
 	return dpp_build_conf_obj_legacy(auth, conf);
 }
 
 
-static struct wpabuf *
+struct wpabuf *
 dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
-		    u16 e_nonce_len, enum dpp_netrole netrole)
+		    u16 e_nonce_len, enum dpp_netrole netrole, bool cert_req)
 {
 	struct wpabuf *conf = NULL, *conf2 = NULL, *env_data = NULL;
 	size_t clear_len, attr_len;
@@ -1605,21 +1711,33 @@
 	size_t len[1];
 	enum dpp_status_error status;
 
+	if (auth->force_conf_resp_status != DPP_STATUS_OK) {
+		status = auth->force_conf_resp_status;
+		goto forced_status;
+	}
+
 	if (netrole == DPP_NETROLE_CONFIGURATOR) {
 #ifdef CONFIG_DPP2
 		env_data = dpp_build_enveloped_data(auth);
 #endif /* CONFIG_DPP2 */
 	} else {
-		conf = dpp_build_conf_obj(auth, netrole, 0);
+		conf = dpp_build_conf_obj(auth, netrole, 0, cert_req);
 		if (conf) {
 			wpa_hexdump_ascii(MSG_DEBUG,
 					  "DPP: configurationObject JSON",
 					  wpabuf_head(conf), wpabuf_len(conf));
-			conf2 = dpp_build_conf_obj(auth, netrole, 1);
+			conf2 = dpp_build_conf_obj(auth, netrole, 1, cert_req);
 		}
 	}
-	status = (conf || env_data) ? DPP_STATUS_OK :
-		DPP_STATUS_CONFIGURE_FAILURE;
+
+	if (conf || env_data)
+		status = DPP_STATUS_OK;
+	else if (!cert_req && netrole == DPP_NETROLE_STA && auth->conf_sta &&
+		 auth->conf_sta->akm == DPP_AKM_DOT1X && !auth->waiting_csr)
+		status = DPP_STATUS_CSR_NEEDED;
+	else
+		status = DPP_STATUS_CONFIGURE_FAILURE;
+forced_status:
 	auth->conf_resp_status = status;
 
 	/* { E-nonce, configurationObject[, sendConnStatus]}ke */
@@ -1633,6 +1751,9 @@
 	if (auth->peer_version >= 2 && auth->send_conn_status &&
 	    netrole == DPP_NETROLE_STA)
 		clear_len += 4;
+	if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta &&
+	    auth->conf_sta->csrattrs)
+		clear_len += 4 + os_strlen(auth->conf_sta->csrattrs);
 	clear = wpabuf_alloc(clear_len);
 	attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -1695,12 +1816,21 @@
 	}
 
 	if (auth->peer_version >= 2 && auth->send_conn_status &&
-	    netrole == DPP_NETROLE_STA) {
+	    netrole == DPP_NETROLE_STA && status == DPP_STATUS_OK) {
 		wpa_printf(MSG_DEBUG, "DPP: sendConnStatus");
 		wpabuf_put_le16(clear, DPP_ATTR_SEND_CONN_STATUS);
 		wpabuf_put_le16(clear, 0);
 	}
 
+	if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta &&
+	    auth->conf_sta->csrattrs) {
+		auth->waiting_csr = true;
+		wpa_printf(MSG_DEBUG, "DPP: CSR Attributes Request");
+		wpabuf_put_le16(clear, DPP_ATTR_CSR_ATTR_REQ);
+		wpabuf_put_le16(clear, os_strlen(auth->conf_sta->csrattrs));
+		wpabuf_put_str(clear, auth->conf_sta->csrattrs);
+	}
+
 #ifdef CONFIG_TESTING_OPTIONS
 skip_config_obj:
 	if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
@@ -1771,6 +1901,7 @@
 	struct wpabuf *resp = NULL;
 	struct json_token *root = NULL, *token;
 	enum dpp_netrole netrole;
+	struct wpabuf *cert_req = NULL;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
@@ -1879,6 +2010,7 @@
 		dpp_auth_fail(auth, "Unsupported netRole");
 		goto fail;
 	}
+	auth->e_netrole = netrole;
 
 	token = json_get_member(root, "mudurl");
 	if (token && token->type == JSON_STRING) {
@@ -1930,9 +2062,56 @@
 			txt);
 	}
 
-	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole);
+#ifdef CONFIG_DPP2
+	cert_req = json_get_member_base64(root, "pkcs10");
+	if (cert_req) {
+		char *txt;
+		int id;
+
+		wpa_hexdump_buf(MSG_DEBUG, "DPP: CertificateRequest", cert_req);
+		if (dpp_validate_csr(auth, cert_req) < 0) {
+			wpa_printf(MSG_DEBUG, "DPP: CSR is not valid");
+			auth->force_conf_resp_status = DPP_STATUS_CSR_BAD;
+			goto cont;
+		}
+
+		if (auth->peer_bi) {
+			id = auth->peer_bi->id;
+		} else if (auth->tmp_peer_bi) {
+			id = auth->tmp_peer_bi->id;
+		} else {
+			struct dpp_bootstrap_info *bi;
+
+			bi = os_zalloc(sizeof(*bi));
+			if (!bi)
+				goto fail;
+			bi->id = dpp_next_id(auth->global);
+			dl_list_add(&auth->global->bootstrap, &bi->list);
+			auth->tmp_peer_bi = bi;
+			id = bi->id;
+		}
+
+		wpa_printf(MSG_DEBUG, "DPP: CSR is valid - forward to CA/RA");
+		txt = base64_encode_no_lf(wpabuf_head(cert_req),
+					  wpabuf_len(cert_req), NULL);
+		if (!txt)
+			goto fail;
+
+		wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_CSR "peer=%d csr=%s",
+			id, txt);
+		os_free(txt);
+		auth->waiting_csr = false;
+		auth->waiting_cert = true;
+		goto fail;
+	}
+cont:
+#endif /* CONFIG_DPP2 */
+
+	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole,
+				   cert_req);
 
 fail:
+	wpabuf_free(cert_req);
 	json_free(root);
 	os_free(unwrapped);
 	return resp;
@@ -2255,6 +2434,20 @@
 }
 
 
+static void dpp_copy_ppkey(struct dpp_config_obj *conf, EVP_PKEY *ppkey)
+{
+	unsigned char *der = NULL;
+	int der_len;
+
+	der_len = i2d_PUBKEY(ppkey, &der);
+	if (der_len <= 0)
+		return;
+	wpabuf_free(conf->pp_key);
+	conf->pp_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+}
+
+
 static void dpp_copy_netaccesskey(struct dpp_authentication *auth,
 				  struct dpp_config_obj *conf)
 {
@@ -2290,10 +2483,10 @@
 			      struct json_token *cred)
 {
 	struct dpp_signed_connector_info info;
-	struct json_token *token, *csign;
+	struct json_token *token, *csign, *ppkey;
 	int ret = -1;
-	EVP_PKEY *csign_pub = NULL;
-	const struct dpp_curve_params *key_curve = NULL;
+	EVP_PKEY *csign_pub = NULL, *pp_pub = NULL;
+	const struct dpp_curve_params *key_curve = NULL, *pp_curve = NULL;
 	const char *signed_connector;
 
 	os_memset(&info, 0, sizeof(info));
@@ -2320,6 +2513,21 @@
 	}
 	dpp_debug_print_key("DPP: Received C-sign-key", csign_pub);
 
+	ppkey = json_get_member(cred, "ppKey");
+	if (ppkey && ppkey->type == JSON_OBJECT) {
+		pp_pub = dpp_parse_jwk(ppkey, &pp_curve);
+		if (!pp_pub) {
+			wpa_printf(MSG_DEBUG, "DPP: Failed to parse ppKey JWK");
+			goto fail;
+		}
+		dpp_debug_print_key("DPP: Received ppKey", pp_pub);
+		if (key_curve != pp_curve) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: C-sign-key and ppKey do not use the same curve");
+			goto fail;
+		}
+	}
+
 	token = json_get_member(cred, "signedConnector");
 	if (!token || token->type != JSON_STRING) {
 		wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found");
@@ -2350,17 +2558,72 @@
 	conf->connector = os_strdup(signed_connector);
 
 	dpp_copy_csign(conf, csign_pub);
+	if (pp_pub)
+		dpp_copy_ppkey(conf, pp_pub);
 	if (dpp_akm_dpp(conf->akm) || auth->peer_version >= 2)
 		dpp_copy_netaccesskey(auth, conf);
 
 	ret = 0;
 fail:
 	EVP_PKEY_free(csign_pub);
+	EVP_PKEY_free(pp_pub);
 	os_free(info.payload);
 	return ret;
 }
 
 
+#ifdef CONFIG_DPP2
+static int dpp_parse_cred_dot1x(struct dpp_authentication *auth,
+				struct dpp_config_obj *conf,
+				struct json_token *cred)
+{
+	struct json_token *ent, *name;
+
+	ent = json_get_member(cred, "entCreds");
+	if (!ent || ent->type != JSON_OBJECT) {
+		dpp_auth_fail(auth, "No entCreds in JSON");
+		return -1;
+	}
+
+	conf->certbag = json_get_member_base64(ent, "certBag");
+	if (!conf->certbag) {
+		dpp_auth_fail(auth, "No certBag in JSON");
+		return -1;
+	}
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received certBag", conf->certbag);
+	conf->certs = dpp_pkcs7_certs(conf->certbag);
+	if (!conf->certs) {
+		dpp_auth_fail(auth, "No certificates in certBag");
+		return -1;
+	}
+
+	conf->cacert = json_get_member_base64(ent, "caCert");
+	if (conf->cacert)
+		wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received caCert",
+				conf->cacert);
+
+	name = json_get_member(ent, "trustedEapServerName");
+	if (name &&
+	    (name->type != JSON_STRING ||
+	     has_ctrl_char((const u8 *) name->string,
+			   os_strlen(name->string)))) {
+		dpp_auth_fail(auth,
+			      "Invalid trustedEapServerName type in JSON");
+		return -1;
+	}
+	if (name && name->string) {
+		wpa_printf(MSG_DEBUG, "DPP: Received trustedEapServerName: %s",
+			   name->string);
+		conf->server_name = os_strdup(name->string);
+		if (!conf->server_name)
+			return -1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_DPP2 */
+
+
 const char * dpp_akm_str(enum dpp_akm akm)
 {
 	switch (akm) {
@@ -2376,6 +2639,8 @@
 		return "dpp+sae";
 	case DPP_AKM_PSK_SAE_DPP:
 		return "dpp+psk+sae";
+	case DPP_AKM_DOT1X:
+		return "dot1x";
 	default:
 		return "??";
 	}
@@ -2397,6 +2662,8 @@
 		return "506F9A02+000FAC08";
 	case DPP_AKM_PSK_SAE_DPP:
 		return "506F9A02+000FAC08+000FAC02+000FAC06";
+	case DPP_AKM_DOT1X:
+		return "000FAC01+000FAC05";
 	default:
 		return "??";
 	}
@@ -2406,7 +2673,7 @@
 static enum dpp_akm dpp_akm_from_str(const char *akm)
 {
 	const char *pos;
-	int dpp = 0, psk = 0, sae = 0;
+	int dpp = 0, psk = 0, sae = 0, dot1x = 0;
 
 	if (os_strcmp(akm, "psk") == 0)
 		return DPP_AKM_PSK;
@@ -2420,6 +2687,8 @@
 		return DPP_AKM_SAE_DPP;
 	if (os_strcmp(akm, "dpp+psk+sae") == 0)
 		return DPP_AKM_PSK_SAE_DPP;
+	if (os_strcmp(akm, "dot1x") == 0)
+		return DPP_AKM_DOT1X;
 
 	pos = akm;
 	while (*pos) {
@@ -2433,6 +2702,10 @@
 			psk = 1;
 		else if (os_strncasecmp(pos, "000FAC08", 8) == 0)
 			sae = 1;
+		else if (os_strncasecmp(pos, "000FAC01", 8) == 0)
+			dot1x = 1;
+		else if (os_strncasecmp(pos, "000FAC05", 8) == 0)
+			dot1x = 1;
 		pos += 8;
 		if (*pos != '+')
 			break;
@@ -2451,6 +2724,8 @@
 		return DPP_AKM_SAE;
 	if (psk)
 		return DPP_AKM_PSK;
+	if (dot1x)
+		return DPP_AKM_DOT1X;
 
 	return DPP_AKM_UNKNOWN;
 }
@@ -2568,6 +2843,12 @@
 		   (auth->peer_version >= 2 && dpp_akm_legacy(conf->akm))) {
 		if (dpp_parse_cred_dpp(auth, conf, cred) < 0)
 			goto fail;
+#ifdef CONFIG_DPP2
+	} else if (conf->akm == DPP_AKM_DOT1X) {
+		if (dpp_parse_cred_dot1x(auth, conf, cred) < 0 ||
+		    dpp_parse_cred_dpp(auth, conf, cred) < 0)
+			goto fail;
+#endif /* CONFIG_DPP2 */
 	} else {
 		wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
 			   token->string);
@@ -2584,6 +2865,20 @@
 }
 
 
+#ifdef CONFIG_DPP2
+static u8 * dpp_get_csr_attrs(const u8 *attrs, size_t attrs_len, size_t *len)
+{
+	const u8 *b64;
+	u16 b64_len;
+
+	b64 = dpp_get_attr(attrs, attrs_len, DPP_ATTR_CSR_ATTR_REQ, &b64_len);
+	if (!b64)
+		return NULL;
+	return base64_decode((const char *) b64, b64_len, len);
+}
+#endif /* CONFIG_DPP2 */
+
+
 int dpp_conf_resp_rx(struct dpp_authentication *auth,
 		     const struct wpabuf *resp)
 {
@@ -2661,6 +2956,28 @@
 	}
 	auth->conf_resp_status = status[0];
 	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+#ifdef CONFIG_DPP2
+	if (status[0] == DPP_STATUS_CSR_NEEDED) {
+		u8 *csrattrs;
+		size_t csrattrs_len;
+
+		wpa_printf(MSG_DEBUG, "DPP: Configurator requested CSR");
+
+		csrattrs = dpp_get_csr_attrs(unwrapped, unwrapped_len,
+					     &csrattrs_len);
+		if (!csrattrs) {
+			dpp_auth_fail(auth,
+				      "Missing or invalid CSR Attributes Request attribute");
+			goto fail;
+		}
+		wpa_hexdump(MSG_DEBUG, "DPP: CsrAttrs", csrattrs, csrattrs_len);
+		os_free(auth->csrattrs);
+		auth->csrattrs = csrattrs;
+		auth->csrattrs_len = csrattrs_len;
+		ret = -2;
+		goto fail;
+	}
+#endif /* CONFIG_DPP2 */
 	if (status[0] != DPP_STATUS_OK) {
 		dpp_auth_fail(auth, "Configurator rejected configuration");
 		goto fail;
@@ -3086,6 +3403,7 @@
 	os_free(conf->kid);
 	os_free(conf->connector);
 	EVP_PKEY_free(conf->connector_key);
+	EVP_PKEY_free(conf->pp_key);
 	os_free(conf);
 }
 
@@ -3144,9 +3462,9 @@
 }
 
 
-struct dpp_configurator *
+static struct dpp_configurator *
 dpp_keygen_configurator(const char *curve, const u8 *privkey,
-			size_t privkey_len)
+			size_t privkey_len, const u8 *pp_key, size_t pp_key_len)
 {
 	struct dpp_configurator *conf;
 
@@ -3166,7 +3484,12 @@
 					      privkey_len);
 	else
 		conf->csign = dpp_gen_keypair(conf->curve);
-	if (!conf->csign)
+	if (pp_key)
+		conf->pp_key = dpp_set_keypair(&conf->curve, pp_key,
+					       pp_key_len);
+	else
+		conf->pp_key = dpp_gen_keypair(conf->curve);
+	if (!conf->csign || !conf->pp_key)
 		goto fail;
 	conf->own = 1;
 
@@ -3207,7 +3530,7 @@
 	auth->peer_protocol_key = auth->own_protocol_key;
 	dpp_copy_csign(&auth->conf_obj[0], auth->conf->csign);
 
-	conf_obj = dpp_build_conf_obj(auth, ap, 0);
+	conf_obj = dpp_build_conf_obj(auth, ap, 0, NULL);
 	if (!conf_obj) {
 		wpabuf_free(auth->conf_obj[0].c_sign_key);
 		auth->conf_obj[0].c_sign_key = NULL;
@@ -3762,11 +4085,11 @@
 	u8 op_class, channel;
 	char chan[20];
 
-	if (peer_bi->num_freq == 0)
+	if (peer_bi->num_freq == 0 && !peer_bi->channels_listed)
 		return 0; /* no channel preference/constraint */
 
 	for (i = 0; i < peer_bi->num_freq; i++) {
-		if (own_bi->num_freq == 0 ||
+		if ((own_bi->num_freq == 0 && !own_bi->channels_listed) ||
 		    freq_included(own_bi->freq, own_bi->num_freq,
 				  peer_bi->freq[i])) {
 			freq = peer_bi->freq[i];
@@ -3853,14 +4176,15 @@
 int dpp_configurator_add(struct dpp_global *dpp, const char *cmd)
 {
 	char *curve = NULL;
-	char *key = NULL;
-	u8 *privkey = NULL;
-	size_t privkey_len = 0;
+	char *key = NULL, *ppkey = NULL;
+	u8 *privkey = NULL, *pp_key = NULL;
+	size_t privkey_len = 0, pp_key_len = 0;
 	int ret = -1;
 	struct dpp_configurator *conf = NULL;
 
 	curve = get_param(cmd, " curve=");
 	key = get_param(cmd, " key=");
+	ppkey = get_param(cmd, " ppkey=");
 
 	if (key) {
 		privkey_len = os_strlen(key) / 2;
@@ -3870,7 +4194,16 @@
 			goto fail;
 	}
 
-	conf = dpp_keygen_configurator(curve, privkey, privkey_len);
+	if (ppkey) {
+		pp_key_len = os_strlen(key) / 2;
+		pp_key = os_malloc(pp_key_len);
+		if (!pp_key ||
+		    hexstr2bin(ppkey, pp_key, pp_key_len) < 0)
+			goto fail;
+	}
+
+	conf = dpp_keygen_configurator(curve, privkey, privkey_len,
+				       pp_key, pp_key_len);
 	if (!conf)
 		goto fail;
 
@@ -3881,7 +4214,9 @@
 fail:
 	os_free(curve);
 	str_clear_free(key);
+	str_clear_free(ppkey);
 	bin_clear_free(privkey, privkey_len);
+	bin_clear_free(pp_key, pp_key_len);
 	dpp_configurator_free(conf);
 	return ret;
 }
@@ -3945,12 +4280,12 @@
 				 struct dpp_asymmetric_key *key)
 {
 	struct dpp_configurator *conf;
-	const EC_KEY *eckey;
-	const EC_GROUP *group;
+	const EC_KEY *eckey, *eckey_pp;
+	const EC_GROUP *group, *group_pp;
 	int nid;
 	const struct dpp_curve_params *curve;
 
-	if (!key->csign)
+	if (!key->csign || !key->pp_key)
 		return -1;
 	eckey = EVP_PKEY_get0_EC_KEY(key->csign);
 	if (!eckey)
@@ -3964,6 +4299,18 @@
 		wpa_printf(MSG_INFO, "DPP: Unsupported group in c-sign-key");
 		return -1;
 	}
+	eckey_pp = EVP_PKEY_get0_EC_KEY(key->pp_key);
+	if (!eckey_pp)
+		return -1;
+	group_pp = EC_KEY_get0_group(eckey_pp);
+	if (!group_pp)
+		return -1;
+	if (EC_GROUP_get_curve_name(group) !=
+	    EC_GROUP_get_curve_name(group_pp)) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Mismatch in c-sign-key and ppKey groups");
+		return -1;
+	}
 
 	conf = os_zalloc(sizeof(*conf));
 	if (!conf)
@@ -3971,6 +4318,8 @@
 	conf->curve = curve;
 	conf->csign = key->csign;
 	key->csign = NULL;
+	conf->pp_key = key->pp_key;
+	key->pp_key = NULL;
 	conf->own = 1;
 	if (dpp_configurator_gen_kid(conf) < 0) {
 		dpp_configurator_free(conf);
@@ -4009,10 +4358,8 @@
 	dpp = os_zalloc(sizeof(*dpp));
 	if (!dpp)
 		return NULL;
-	dpp->msg_ctx = config->msg_ctx;
 #ifdef CONFIG_DPP2
 	dpp->cb_ctx = config->cb_ctx;
-	dpp->process_conf_obj = config->process_conf_obj;
 	dpp->remove_bi = config->remove_bi;
 #endif /* CONFIG_DPP2 */
 
@@ -4050,6 +4397,7 @@
 
 
 #ifdef CONFIG_DPP2
+
 struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi)
 {
 	struct wpabuf *msg;
@@ -4066,4 +4414,17 @@
 			"DPP: Presence Announcement frame attributes", msg);
 	return msg;
 }
+
+
+void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src,
+				unsigned int freq, const u8 *hash)
+{
+	char hex[SHA256_MAC_LEN * 2 + 1];
+
+	wpa_snprintf_hex(hex, sizeof(hex), hash, SHA256_MAC_LEN);
+	wpa_msg(msg_ctx, MSG_INFO,
+		DPP_EVENT_CHIRP_RX "id=%d src=" MACSTR " freq=%u hash=%s",
+		id, MAC2STR(src), freq, hex);
+}
+
 #endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp.h b/src/common/dpp.h
index d77762e..99a1ca7 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -21,6 +21,7 @@
 struct hostapd_ip_addr;
 struct dpp_global;
 struct json_token;
+struct dpp_reconfig_id;
 
 #ifdef CONFIG_TESTING_OPTIONS
 #define DPP_VERSION (dpp_version_override)
@@ -87,6 +88,9 @@
 	DPP_ATTR_RECONFIG_FLAGS = 0x101D,
 	DPP_ATTR_C_SIGN_KEY_HASH = 0x101E,
 	DPP_ATTR_CSR_ATTR_REQ = 0x101F,
+	DPP_ATTR_A_NONCE = 0x1020,
+	DPP_ATTR_E_PRIME_ID = 0x1021,
+	DPP_ATTR_CONFIGURATOR_NONCE = 0x1022,
 };
 
 enum dpp_status_error {
@@ -120,6 +124,7 @@
 #define DPP_MAX_NONCE_LEN 32
 #define DPP_MAX_HASH_LEN 64
 #define DPP_MAX_SHARED_SECRET_LEN 66
+#define DPP_CP_LEN 64
 
 struct dpp_curve_params {
 	const char *name;
@@ -149,6 +154,7 @@
 	char *pk;
 	unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
 	unsigned int num_freq;
+	bool channels_listed;
 	u8 version;
 	int own;
 	EVP_PKEY *pubkey;
@@ -196,6 +202,7 @@
 	DPP_AKM_PSK_SAE,
 	DPP_AKM_SAE_DPP,
 	DPP_AKM_PSK_SAE_DPP,
+	DPP_AKM_DOT1X,
 };
 
 enum dpp_netrole {
@@ -221,11 +228,14 @@
 	char *passphrase;
 	u8 psk[32];
 	int psk_set;
+
+	char *csrattrs;
 };
 
 struct dpp_asymmetric_key {
 	struct dpp_asymmetric_key *next;
 	EVP_PKEY *csign;
+	EVP_PKEY *pp_key;
 	char *config_template;
 	char *connector_template;
 };
@@ -241,18 +251,22 @@
 	struct dpp_bootstrap_info *peer_bi;
 	struct dpp_bootstrap_info *own_bi;
 	struct dpp_bootstrap_info *tmp_own_bi;
+	struct dpp_bootstrap_info *tmp_peer_bi;
 	u8 waiting_pubkey_hash[SHA256_MAC_LEN];
 	int response_pending;
 	int reconfig;
 	enum dpp_connector_key reconfig_connector_key;
 	enum dpp_status_error auth_resp_status;
 	enum dpp_status_error conf_resp_status;
+	enum dpp_status_error force_conf_resp_status;
 	u8 peer_mac_addr[ETH_ALEN];
 	u8 i_nonce[DPP_MAX_NONCE_LEN];
 	u8 r_nonce[DPP_MAX_NONCE_LEN];
 	u8 e_nonce[DPP_MAX_NONCE_LEN];
+	u8 c_nonce[DPP_MAX_NONCE_LEN];
 	u8 i_capab;
 	u8 r_capab;
+	enum dpp_netrole e_netrole;
 	EVP_PKEY *own_protocol_key;
 	EVP_PKEY *peer_protocol_key;
 	EVP_PKEY *reconfig_old_protocol_key;
@@ -293,6 +307,7 @@
 	bool reconfig_success;
 	struct wpabuf *conf_req;
 	const struct wpabuf *conf_resp; /* owned by GAS server */
+	struct wpabuf *conf_resp_tcp;
 	struct dpp_configuration *conf_ap;
 	struct dpp_configuration *conf2_ap;
 	struct dpp_configuration *conf_sta;
@@ -309,6 +324,11 @@
 		int psk_set;
 		enum dpp_akm akm;
 		struct wpabuf *c_sign_key;
+		struct wpabuf *certbag;
+		struct wpabuf *certs;
+		struct wpabuf *cacert;
+		char *server_name;
+		struct wpabuf *pp_key;
 	} conf_obj[DPP_MAX_CONF_OBJ];
 	unsigned int num_conf_obj;
 	struct dpp_asymmetric_key *conf_key_pkg;
@@ -319,6 +339,16 @@
 	int akm_use_selector;
 	int configurator_set;
 	u8 transaction_id;
+	u8 *csrattrs;
+	size_t csrattrs_len;
+	bool waiting_csr;
+	struct wpabuf *csr;
+	struct wpabuf *priv_key; /* DER-encoded private key used for csr */
+	bool waiting_cert;
+	char *trusted_eap_server_name;
+	struct wpabuf *cacert;
+	struct wpabuf *certbag;
+	void *cert_resp_ctx;
 #ifdef CONFIG_TESTING_OPTIONS
 	char *config_obj_override;
 	char *discovery_override;
@@ -339,6 +369,7 @@
 	const struct dpp_curve_params *curve;
 	char *connector; /* own Connector for reconfiguration */
 	EVP_PKEY *connector_key;
+	EVP_PKEY *pp_key;
 };
 
 struct dpp_introduction {
@@ -351,6 +382,7 @@
 	const struct hostapd_ip_addr *ipaddr;
 	const u8 *pkhash;
 
+	void *msg_ctx;
 	void *cb_ctx;
 	void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
 		   size_t len);
@@ -362,6 +394,11 @@
 	const char *configurator_params;
 	int tcp_port;
 	u8 allowed_roles;
+	int qr_mutual;
+	enum dpp_netrole netrole;
+	void *msg_ctx;
+	void *cb_ctx;
+	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
 };
 
 #ifdef CONFIG_TESTING_OPTIONS
@@ -519,6 +556,10 @@
 int dpp_set_configurator(struct dpp_authentication *auth, const char *cmd);
 void dpp_auth_deinit(struct dpp_authentication *auth);
 struct wpabuf *
+dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
+		    u16 e_nonce_len, enum dpp_netrole netrole,
+		    bool cert_req);
+struct wpabuf *
 dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
 		size_t attr_len);
 int dpp_conf_resp_rx(struct dpp_authentication *auth,
@@ -548,9 +589,6 @@
 int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
 			     size_t buflen);
 void dpp_configurator_free(struct dpp_configurator *conf);
-struct dpp_configurator *
-dpp_keygen_configurator(const char *curve, const u8 *privkey,
-			size_t privkey_len);
 int dpp_configurator_own_config(struct dpp_authentication *auth,
 				const char *curve, int ap);
 enum dpp_status_error
@@ -595,6 +633,11 @@
 int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len);
 void dpp_pfs_free(struct dpp_pfs *pfs);
 
+struct wpabuf * dpp_build_csr(struct dpp_authentication *auth,
+			      const char *name);
+struct wpabuf * dpp_pkcs7_certs(const struct wpabuf *pkcs7);
+int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr);
+
 struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
 					    const char *uri);
 struct dpp_bootstrap_info * dpp_add_nfc_uri(struct dpp_global *dpp,
@@ -634,14 +677,23 @@
 int dpp_controller_start(struct dpp_global *dpp,
 			 struct dpp_controller_config *config);
 void dpp_controller_stop(struct dpp_global *dpp);
+struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp,
+						    unsigned int id);
+void dpp_controller_new_qr_code(struct dpp_global *dpp,
+				struct dpp_bootstrap_info *bi);
 int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
-		 const struct hostapd_ip_addr *addr, int port);
+		 const struct hostapd_ip_addr *addr, int port,
+		 const char *name, enum dpp_netrole netrole, void *msg_ctx,
+		 void *cb_ctx,
+		 int (*process_conf_obj)(void *ctx,
+					 struct dpp_authentication *auth));
+
 struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi);
+void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src,
+				unsigned int freq, const u8 *hash);
 
 struct dpp_global_config {
-	void *msg_ctx;
 	void *cb_ctx;
-	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
 	void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
 };
 
@@ -652,10 +704,15 @@
 /* dpp_reconfig.c */
 
 struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
-						size_t csign_key_len);
+						size_t csign_key_len,
+						const u8 *net_access_key,
+						size_t net_access_key_len,
+						struct dpp_reconfig_id *id);
 struct dpp_authentication *
 dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
-		  struct dpp_configurator *conf, unsigned int freq);
+		  struct dpp_configurator *conf, unsigned int freq, u16 group,
+		  const u8 *a_nonce_attr, size_t a_nonce_len,
+		  const u8 *e_id_attr, size_t e_id_len);
 struct dpp_authentication *
 dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
 			 const char *own_connector,
@@ -669,5 +726,12 @@
 int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
 			      const u8 *attr_start, size_t attr_len);
 
+struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key,
+					     size_t csign_key_len,
+					     const u8 *pp_key,
+					     size_t pp_key_len);
+int dpp_update_reconfig_id(struct dpp_reconfig_id *id);
+void dpp_free_reconfig_id(struct dpp_reconfig_id *id);
+
 #endif /* CONFIG_DPP */
 #endif /* DPP_H */
diff --git a/src/common/dpp_backup.c b/src/common/dpp_backup.c
index c675c42..3b81f09 100644
--- a/src/common/dpp_backup.c
+++ b/src/common/dpp_backup.c
@@ -40,6 +40,7 @@
 		struct dpp_asymmetric_key *next = key->next;
 
 		EVP_PKEY_free(key->csign);
+		EVP_PKEY_free(key->pp_key);
 		str_clear_free(key->config_template);
 		str_clear_free(key->connector_template);
 		os_free(key);
@@ -48,35 +49,62 @@
 }
 
 
-static struct wpabuf * dpp_build_conf_params(void)
+static struct wpabuf * dpp_build_conf_params(struct dpp_configurator *conf)
 {
-	struct wpabuf *buf;
+	struct wpabuf *buf, *priv_key = NULL;
 	size_t len;
 	/* TODO: proper template values */
 	const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
 	const char *connector_template = NULL;
+	EC_KEY *eckey;
+	unsigned char *der = NULL;
+	int der_len;
+
+	if (!conf->pp_key)
+		return NULL;
+	eckey = EVP_PKEY_get0_EC_KEY(conf->pp_key);
+	if (!eckey)
+		return NULL;
+
+	EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len > 0)
+		priv_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+	if (!priv_key)
+		goto fail;
 
 	len = 100 + os_strlen(conf_template);
 	if (connector_template)
 		len += os_strlen(connector_template);
+	if (priv_key)
+		len += wpabuf_len(priv_key);
 	buf = wpabuf_alloc(len);
 	if (!buf)
-		return NULL;
+		goto fail;
 
 	/*
 	 * DPPConfigurationParameters ::= SEQUENCE {
+	 *    privacyProtectionKey      PrivateKey,
 	 *    configurationTemplate	UTF8String,
 	 *    connectorTemplate		UTF8String OPTIONAL}
 	 */
 
+	/* PrivateKey ::= OCTET STRING */
+	asn1_put_octet_string(buf, priv_key);
+
 	asn1_put_utf8string(buf, conf_template);
 	if (connector_template)
 		asn1_put_utf8string(buf, connector_template);
+	wpabuf_clear_free(priv_key);
 	return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+fail:
+	wpabuf_clear_free(priv_key);
+	return NULL;
 }
 
 
-static struct wpabuf * dpp_build_attribute(void)
+static struct wpabuf * dpp_build_attribute(struct dpp_configurator *conf)
 {
 	struct wpabuf *conf_params, *attr;
 
@@ -88,7 +116,7 @@
 	 *    type OBJECT IDENTIFIER,
 	 *    values SET SIZE(1..MAX) OF Type
 	 */
-	conf_params = dpp_build_conf_params();
+	conf_params = dpp_build_conf_params(conf);
 	conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
 				  ASN1_TAG_SET);
 	if (!conf_params)
@@ -167,7 +195,7 @@
 	alg = dpp_build_key_alg(auth->conf->curve);
 
 	/* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
-	attr = dpp_build_attribute();
+	attr = dpp_build_attribute(auth->conf);
 	attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
 	if (!priv_key || !attr || !alg)
 		goto fail;
@@ -189,7 +217,7 @@
 	if (!key)
 		goto fail;
 
-	asn1_put_integer(key, 1); /* version = v2(1) */
+	asn1_put_integer(key, 0); /* version = v1(0) */
 
 	/* PrivateKeyAlgorithmIdentifier */
 	wpabuf_put_buf(key, alg);
@@ -275,18 +303,21 @@
 	struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
 		*key_enc_alg = NULL, *salt;
 	u8 kek[DPP_MAX_HASH_LEN];
-	const u8 *key;
+	u8 key[DPP_MAX_HASH_LEN];
 	size_t key_len;
+	int res;
 
 	salt = wpabuf_alloc(64);
 	if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
 		goto fail;
 	wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
 
-	/* TODO: For initial testing, use ke as the key. Replace this with a
-	 * new key once that has been defined. */
-	key = auth->ke;
 	key_len = auth->curve->hash_len;
+	/* password = HKDF-Expand(bk, "Enveloped Data Password", length) */
+	res = dpp_hkdf_expand(key_len, auth->bk, key_len,
+			      "Enveloped Data Password", key, key_len);
+	if (res < 0)
+		goto fail;
 	wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
 
 	if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
@@ -905,7 +936,7 @@
 	/* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
 	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
 		goto fail;
-	if (val != 1) {
+	if (val != 0 && val != 1) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Unsupported DPPAsymmetricKeyPackage version %d",
 			   val);
@@ -1056,6 +1087,7 @@
 
 	/*
 	 * DPPConfigurationParameters ::= SEQUENCE {
+	 *    privacyProtectionKey      PrivateKey,
 	 *    configurationTemplate	UTF8String,
 	 *    connectorTemplate		UTF8String OPTIONAL}
 	 */
@@ -1072,6 +1104,37 @@
 	end = pos;
 	pos = hdr.payload;
 
+	/*
+	 * PrivateKey ::= OCTET STRING
+	 *    (Contains DER encoding of ECPrivateKey)
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected OCTETSTRING (PrivateKey) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: privacyProtectionKey",
+			hdr.payload, hdr.length);
+	pos = hdr.payload + hdr.length;
+	eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
+	if (!eckey) {
+		wpa_printf(MSG_INFO,
+			   "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	key->pp_key = EVP_PKEY_new();
+	if (!key->pp_key || EVP_PKEY_assign_EC_KEY(key->pp_key, eckey) != 1) {
+		EC_KEY_free(eckey);
+		goto fail;
+	}
+	if (wpa_debug_show_keys)
+		dpp_debug_print_key("DPP: Received privacyProtectionKey",
+				    key->pp_key);
+
 	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 	    hdr.class != ASN1_CLASS_UNIVERSAL ||
 	    hdr.tag != ASN1_TAG_UTF8STRING) {
@@ -1151,7 +1214,7 @@
 int dpp_conf_resp_env_data(struct dpp_authentication *auth,
 			   const u8 *env_data, size_t env_data_len)
 {
-	const u8 *key;
+	u8 key[DPP_MAX_HASH_LEN];
 	size_t key_len;
 	u8 kek[DPP_MAX_HASH_LEN];
 	u8 cont_encr_key[DPP_MAX_HASH_LEN];
@@ -1167,10 +1230,12 @@
 	if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
 		return -1;
 
-	/* TODO: For initial testing, use ke as the key. Replace this with a
-	 * new key once that has been defined. */
-	key = auth->ke;
 	key_len = auth->curve->hash_len;
+	/* password = HKDF-Expand(bk, "Enveloped Data Password", length) */
+	res = dpp_hkdf_expand(key_len, auth->bk, key_len,
+			      "Enveloped Data Password", key, key_len);
+	if (res < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
 
 	if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
index 8bf2a74..7c48015 100644
--- a/src/common/dpp_crypto.c
+++ b/src/common/dpp_crypto.c
@@ -12,12 +12,14 @@
 #include <openssl/err.h>
 #include <openssl/asn1.h>
 #include <openssl/asn1t.h>
+#include <openssl/pem.h>
 
 #include "utils/common.h"
 #include "utils/base64.h"
 #include "utils/json.h"
 #include "common/ieee802_11_defs.h"
 #include "crypto/crypto.h"
+#include "crypto/random.h"
 #include "crypto/sha384.h"
 #include "crypto/sha512.h"
 #include "dpp.h"
@@ -129,6 +131,18 @@
 }
 
 
+const struct dpp_curve_params * dpp_get_curve_ike_group(u16 group)
+{
+	int i;
+
+	for (i = 0; dpp_curves[i].name; i++) {
+		if (dpp_curves[i].ike_group == group)
+			return &dpp_curves[i];
+	}
+	return NULL;
+}
+
+
 void dpp_debug_print_point(const char *title, const EC_GROUP *group,
 			   const EC_POINT *point)
 {
@@ -855,7 +869,7 @@
 			"DPP: bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x])",
 			auth->bk, hash_len);
 
-	/* ke = HKDF-Expand(bkK, "DPP Key", length) */
+	/* ke = HKDF-Expand(bk, "DPP Key", length) */
 	res = dpp_hkdf_expand(hash_len, auth->bk, hash_len, info_ke, auth->ke,
 			      hash_len);
 	if (res < 0)
@@ -2256,6 +2270,7 @@
 	u8 prk[DPP_MAX_HASH_LEN];
 	const struct dpp_curve_params *curve;
 	int res = -1;
+	u8 nonces[2 * DPP_MAX_NONCE_LEN];
 
 	own_key = dpp_set_keypair(&auth->curve, net_access_key,
 				  net_access_key_len);
@@ -2271,7 +2286,7 @@
 
 	if (auth->curve != curve) {
 		wpa_printf(MSG_DEBUG,
-			   "DPP: Mismatching netAccessKey curves (%s != %s)",
+			   "DPP: Mismatching netAccessKey curves (own=%s != peer=%s)",
 			   auth->curve->name, curve->name);
 		goto fail;
 	}
@@ -2280,6 +2295,13 @@
 	if (!auth->own_protocol_key)
 		goto fail;
 
+	if (random_get_bytes(auth->e_nonce, auth->curve->nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "DPP: E-nonce",
+			auth->e_nonce, auth->curve->nonce_len);
+
 	/* M = { cR + pR } * CI */
 	cR = EVP_PKEY_get0_EC_KEY(own_key);
 	pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
@@ -2312,10 +2334,12 @@
 		goto fail;
 	wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
 
-	/* ke = HKDF(I-nonce, "dpp reconfig key", M.x) */
+	/* ke = HKDF(C-nonce | E-nonce, "dpp reconfig key", M.x) */
 
-	/* HKDF-Extract(I-nonce, M.x) */
-	if (dpp_hmac(curve->hash_len, auth->i_nonce, curve->nonce_len,
+	/* HKDF-Extract(C-nonce | E-nonce, M.x) */
+	os_memcpy(nonces, auth->c_nonce, curve->nonce_len);
+	os_memcpy(&nonces[curve->nonce_len], auth->e_nonce, curve->nonce_len);
+	if (dpp_hmac(curve->hash_len, nonces, 2 * curve->nonce_len,
 		     Mx, curve->prime_len, prk) < 0)
 		goto fail;
 	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
@@ -2325,7 +2349,7 @@
 			    "dpp reconfig key", auth->ke, curve->hash_len) < 0)
 		goto fail;
 	wpa_hexdump_key(MSG_DEBUG,
-			"DPP: ke = HKDF(I-nonce, \"dpp reconfig key\", M.x)",
+			"DPP: ke = HKDF(C-nonce | E-nonce, \"dpp reconfig key\", M.x)",
 			auth->ke, curve->hash_len);
 
 	res = 0;
@@ -2362,6 +2386,7 @@
 	u8 prk[DPP_MAX_HASH_LEN];
 	int res = -1;
 	const struct dpp_curve_params *curve;
+	u8 nonces[2 * DPP_MAX_NONCE_LEN];
 
 	pr = dpp_set_pubkey_point(auth->conf->connector_key,
 				  r_proto, r_proto_len);
@@ -2380,7 +2405,7 @@
 	dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
 	if (auth->curve != curve) {
 		wpa_printf(MSG_DEBUG,
-			   "DPP: Mismatching netAccessKey curves (%s != %s)",
+			   "DPP: Mismatching netAccessKey curves (own=%s != peer=%s)",
 			   auth->curve->name, curve->name);
 		goto fail;
 	}
@@ -2407,10 +2432,12 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
 
-	/* ke = HKDF(I-nonce, "dpp reconfig key", M.x) */
+	/* ke = HKDF(C-nonce | E-nonce, "dpp reconfig key", M.x) */
 
-	/* HKDF-Extract(I-nonce, M.x) */
-	if (dpp_hmac(curve->hash_len, auth->i_nonce, curve->nonce_len,
+	/* HKDF-Extract(C-nonce | E-nonce, M.x) */
+	os_memcpy(nonces, auth->c_nonce, curve->nonce_len);
+	os_memcpy(&nonces[curve->nonce_len], auth->e_nonce, curve->nonce_len);
+	if (dpp_hmac(curve->hash_len, nonces, 2 * curve->nonce_len,
 		     Mx, curve->prime_len, prk) < 0)
 		goto fail;
 	wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
@@ -2420,7 +2447,7 @@
 			    "dpp reconfig key", auth->ke, curve->hash_len) < 0)
 		goto fail;
 	wpa_hexdump_key(MSG_DEBUG,
-			"DPP: ke = HKDF(I-nonce, \"dpp reconfig key\", M.x)",
+			"DPP: ke = HKDF(C-nonce | E-nonce, \"dpp reconfig key\", M.x)",
 			auth->ke, curve->hash_len);
 
 	res = 0;
@@ -2664,6 +2691,520 @@
 	os_free(pfs);
 }
 
+
+struct wpabuf * dpp_build_csr(struct dpp_authentication *auth, const char *name)
+{
+	X509_REQ *req = NULL;
+	struct wpabuf *buf = NULL;
+	unsigned char *der;
+	int der_len;
+	EVP_PKEY *key;
+	const EVP_MD *sign_md;
+	unsigned int hash_len = auth->curve->hash_len;
+	EC_KEY *eckey;
+	BIO *out = NULL;
+	u8 cp[DPP_CP_LEN];
+	char *password;
+	size_t password_len;
+	int res;
+
+	/* TODO: use auth->csrattrs */
+
+	/* TODO: support generation of a new private key if csrAttrs requests
+	 * a specific group to be used */
+	key = auth->own_protocol_key;
+
+	eckey = EVP_PKEY_get1_EC_KEY(key);
+	if (!eckey)
+		goto fail;
+	der = NULL;
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len <= 0)
+		goto fail;
+	wpabuf_free(auth->priv_key);
+	auth->priv_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+	if (!auth->priv_key)
+		goto fail;
+
+	req = X509_REQ_new();
+	if (!req || !X509_REQ_set_pubkey(req, key))
+		goto fail;
+
+	if (name) {
+		X509_NAME *n;
+
+		n = X509_REQ_get_subject_name(req);
+		if (!n)
+			goto fail;
+
+		if (X509_NAME_add_entry_by_txt(
+			    n, "CN", MBSTRING_UTF8,
+			    (const unsigned char *) name, -1, -1, 0) != 1)
+			goto fail;
+	}
+
+	/* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */
+	if (dpp_hkdf_expand(hash_len, auth->bk, hash_len,
+			    "CSR challengePassword", cp, DPP_CP_LEN) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)",
+			cp, DPP_CP_LEN);
+	password = base64_encode_no_lf(cp, DPP_CP_LEN, &password_len);
+	forced_memzero(cp, DPP_CP_LEN);
+	if (!password)
+		goto fail;
+
+	res = X509_REQ_add1_attr_by_NID(req, NID_pkcs9_challengePassword,
+					V_ASN1_UTF8STRING,
+					(const unsigned char *) password,
+					password_len);
+	bin_clear_free(password, password_len);
+	if (!res)
+		goto fail;
+
+	/* TODO */
+
+	/* TODO: hash func selection based on csrAttrs */
+	if (hash_len == SHA256_MAC_LEN) {
+		sign_md = EVP_sha256();
+	} else if (hash_len == SHA384_MAC_LEN) {
+		sign_md = EVP_sha384();
+	} else if (hash_len == SHA512_MAC_LEN) {
+		sign_md = EVP_sha512();
+	} else {
+		wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+		goto fail;
+	}
+
+	if (!X509_REQ_sign(req, key, sign_md))
+		goto fail;
+
+	der = NULL;
+	der_len = i2d_X509_REQ(req, &der);
+	if (der_len < 0)
+		goto fail;
+	buf = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: CSR", buf);
+
+fail:
+	BIO_free_all(out);
+	X509_REQ_free(req);
+	return buf;
+}
+
+
+struct wpabuf * dpp_pkcs7_certs(const struct wpabuf *pkcs7)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+	CBS pkcs7_cbs;
+#else /* OPENSSL_IS_BORINGSSL */
+	PKCS7 *p7 = NULL;
+	const unsigned char *p = wpabuf_head(pkcs7);
+#endif /* OPENSSL_IS_BORINGSSL */
+	STACK_OF(X509) *certs;
+	int i, num;
+	BIO *out = NULL;
+	size_t rlen;
+	struct wpabuf *pem = NULL;
+	int res;
+
+#ifdef OPENSSL_IS_BORINGSSL
+	certs = sk_X509_new_null();
+	if (!certs)
+		goto fail;
+	CBS_init(&pkcs7_cbs, wpabuf_head(pkcs7), wpabuf_len(pkcs7));
+	if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
+		wpa_printf(MSG_INFO, "DPP: Could not parse PKCS#7 object: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+#else /* OPENSSL_IS_BORINGSSL */
+	p7 = d2i_PKCS7(NULL, &p, wpabuf_len(pkcs7));
+	if (!p7) {
+		wpa_printf(MSG_INFO, "DPP: Could not parse PKCS#7 object: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	switch (OBJ_obj2nid(p7->type)) {
+	case NID_pkcs7_signed:
+		certs = p7->d.sign->cert;
+		break;
+	case NID_pkcs7_signedAndEnveloped:
+		certs = p7->d.signed_and_enveloped->cert;
+		break;
+	default:
+		certs = NULL;
+		break;
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+
+	if (!certs || ((num = sk_X509_num(certs)) == 0)) {
+		wpa_printf(MSG_INFO,
+			   "DPP: No certificates found in PKCS#7 object");
+		goto fail;
+	}
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		goto fail;
+
+	for (i = 0; i < num; i++) {
+		X509 *cert = sk_X509_value(certs, i);
+
+		PEM_write_bio_X509(out, cert);
+	}
+
+	rlen = BIO_ctrl_pending(out);
+	pem = wpabuf_alloc(rlen);
+	if (!pem)
+		goto fail;
+	res = BIO_read(out, wpabuf_put(pem, 0), rlen);
+	if (res <= 0) {
+		wpabuf_free(pem);
+		goto fail;
+	}
+	wpabuf_put(pem, res);
+
+fail:
+#ifdef OPENSSL_IS_BORINGSSL
+	if (certs)
+		sk_X509_pop_free(certs, X509_free);
+#else /* OPENSSL_IS_BORINGSSL */
+	PKCS7_free(p7);
+#endif /* OPENSSL_IS_BORINGSSL */
+	if (out)
+		BIO_free_all(out);
+
+	return pem;
+}
+
+
+int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr)
+{
+	X509_REQ *req;
+	const unsigned char *pos;
+	EVP_PKEY *pkey;
+	int res, loc, ret = -1;
+	X509_ATTRIBUTE *attr;
+	ASN1_TYPE *type;
+	ASN1_STRING *str;
+	unsigned char *utf8 = NULL;
+	unsigned char *cp = NULL;
+	size_t cp_len;
+	u8 exp_cp[DPP_CP_LEN];
+	unsigned int hash_len = auth->curve->hash_len;
+
+	pos = wpabuf_head(csr);
+	req = d2i_X509_REQ(NULL, &pos, wpabuf_len(csr));
+	if (!req) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to parse CSR");
+		return -1;
+	}
+
+	pkey = X509_REQ_get_pubkey(req);
+	if (!pkey) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to get public key from CSR");
+		goto fail;
+	}
+
+	res = X509_REQ_verify(req, pkey);
+	EVP_PKEY_free(pkey);
+	if (res != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: CSR does not have a valid signature");
+		goto fail;
+	}
+
+	loc = X509_REQ_get_attr_by_NID(req, NID_pkcs9_challengePassword, -1);
+	if (loc < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: CSR does not include challengePassword");
+		goto fail;
+	}
+
+	attr = X509_REQ_get_attr(req, loc);
+	if (!attr) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not get challengePassword attribute");
+		goto fail;
+	}
+
+	type = X509_ATTRIBUTE_get0_type(attr, 0);
+	if (!type) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not get challengePassword attribute type");
+		goto fail;
+	}
+
+	res = ASN1_TYPE_get(type);
+	/* This is supposed to be UTF8String, but allow other strings as well
+	 * since challengePassword is using ASCII (base64 encoded). */
+	if (res != V_ASN1_UTF8STRING && res != V_ASN1_PRINTABLESTRING &&
+	    res != V_ASN1_IA5STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected challengePassword attribute type %d",
+			   res);
+		goto fail;
+	}
+
+	str = X509_ATTRIBUTE_get0_data(attr, 0, res, NULL);
+	if (!str) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not get ASN.1 string for challengePassword");
+		goto fail;
+	}
+
+	res = ASN1_STRING_to_UTF8(&utf8, str);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not get UTF8 version of challengePassword");
+		goto fail;
+	}
+
+	cp = base64_decode((const char *) utf8, res, &cp_len);
+	OPENSSL_free(utf8);
+	if (!cp) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not base64 decode challengePassword");
+		goto fail;
+	}
+	if (cp_len != DPP_CP_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected cp length (%zu) in CSR challengePassword",
+			   cp_len);
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "DPP: cp from CSR challengePassword",
+			cp, cp_len);
+
+	/* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */
+	if (dpp_hkdf_expand(hash_len, auth->bk, hash_len,
+			    "CSR challengePassword", exp_cp, DPP_CP_LEN) < 0)
+		goto fail;
+	wpa_hexdump_key(MSG_DEBUG,
+			"DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)",
+			exp_cp, DPP_CP_LEN);
+	if (os_memcmp_const(cp, exp_cp, DPP_CP_LEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: CSR challengePassword does not match calculated cp");
+		goto fail;
+	}
+
+	ret = 0;
+fail:
+	os_free(cp);
+	X509_REQ_free(req);
+	return ret;
+}
+
+
+struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key,
+					     size_t csign_key_len,
+					     const u8 *pp_key,
+					     size_t pp_key_len)
+{
+	const unsigned char *p;
+	EVP_PKEY *csign = NULL, *ppkey = NULL;
+	struct dpp_reconfig_id *id = NULL;
+	BN_CTX *ctx = NULL;
+	BIGNUM *bn = NULL, *q = NULL;
+	const EC_KEY *eckey;
+	const EC_GROUP *group;
+	EC_POINT *e_id = NULL;
+
+	p = csign_key;
+	csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+	if (!csign)
+		goto fail;
+
+	if (!pp_key)
+		goto fail;
+	p = pp_key;
+	ppkey = d2i_PUBKEY(NULL, &p, pp_key_len);
+	if (!ppkey)
+		goto fail;
+
+	eckey = EVP_PKEY_get0_EC_KEY(csign);
+	if (!eckey)
+		goto fail;
+	group = EC_KEY_get0_group(eckey);
+	if (!group)
+		goto fail;
+
+	e_id = EC_POINT_new(group);
+	ctx = BN_CTX_new();
+	bn = BN_new();
+	q = BN_new();
+	if (!e_id || !ctx || !bn || !q ||
+	    !EC_GROUP_get_order(group, q, ctx) ||
+	    !BN_rand_range(bn, q) ||
+	    !EC_POINT_mul(group, e_id, bn, NULL, NULL, ctx))
+		goto fail;
+
+	dpp_debug_print_point("DPP: Generated random point E-id", group, e_id);
+
+	id = os_zalloc(sizeof(*id));
+	if (!id)
+		goto fail;
+	id->group = group;
+	id->e_id = e_id;
+	e_id = NULL;
+	id->csign = csign;
+	csign = NULL;
+	id->pp_key = ppkey;
+	ppkey = NULL;
+fail:
+	EC_POINT_free(e_id);
+	EVP_PKEY_free(csign);
+	EVP_PKEY_free(ppkey);
+	BN_clear_free(bn);
+	BN_CTX_free(ctx);
+	return id;
+}
+
+
+static EVP_PKEY * dpp_pkey_from_point(const EC_GROUP *group,
+				      const EC_POINT *point)
+{
+	EC_KEY *eckey;
+	EVP_PKEY *pkey = NULL;
+
+	eckey = EC_KEY_new();
+	if (!eckey ||
+	    EC_KEY_set_group(eckey, group) != 1 ||
+	    EC_KEY_set_public_key(eckey, point) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "DPP: Failed to set EC_KEY: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+	pkey = EVP_PKEY_new();
+	if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+		wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+		EVP_PKEY_free(pkey);
+		pkey = NULL;
+		goto fail;
+	}
+
+fail:
+	EC_KEY_free(eckey);
+	return pkey;
+}
+
+
+int dpp_update_reconfig_id(struct dpp_reconfig_id *id)
+{
+	BN_CTX *ctx = NULL;
+	BIGNUM *bn = NULL, *q = NULL;
+	EC_POINT *e_prime_id = NULL, *a_nonce = NULL;
+	int ret = -1;
+	const EC_KEY *pp;
+	const EC_POINT *pp_point;
+
+	pp = EVP_PKEY_get0_EC_KEY(id->pp_key);
+	if (!pp)
+		goto fail;
+	pp_point = EC_KEY_get0_public_key(pp);
+	e_prime_id = EC_POINT_new(id->group);
+	a_nonce = EC_POINT_new(id->group);
+	ctx = BN_CTX_new();
+	bn = BN_new();
+	q = BN_new();
+	/* Generate random 0 <= a-nonce < q
+	 * A-NONCE = a-nonce * G
+	 * E'-id = E-id + a-nonce * P_pk */
+	if (!pp_point || !e_prime_id || !a_nonce || !ctx || !bn || !q ||
+	    !EC_GROUP_get_order(id->group, q, ctx) ||
+	    !BN_rand_range(bn, q) || /* bn = a-nonce */
+	    !EC_POINT_mul(id->group, a_nonce, bn, NULL, NULL, ctx) ||
+	    !EC_POINT_mul(id->group, e_prime_id, NULL, pp_point, bn, ctx) ||
+	    !EC_POINT_add(id->group, e_prime_id, id->e_id, e_prime_id, ctx))
+		goto fail;
+
+	dpp_debug_print_point("DPP: Generated A-NONCE", id->group, a_nonce);
+	dpp_debug_print_point("DPP: Encrypted E-id to E'-id",
+			      id->group, e_prime_id);
+
+	EVP_PKEY_free(id->a_nonce);
+	EVP_PKEY_free(id->e_prime_id);
+	id->a_nonce = dpp_pkey_from_point(id->group, a_nonce);
+	id->e_prime_id = dpp_pkey_from_point(id->group, e_prime_id);
+	if (!id->a_nonce || !id->e_prime_id)
+		goto fail;
+
+	ret = 0;
+
+fail:
+	EC_POINT_free(e_prime_id);
+	EC_POINT_free(a_nonce);
+	BN_clear_free(bn);
+	BN_CTX_free(ctx);
+	return ret;
+}
+
+
+void dpp_free_reconfig_id(struct dpp_reconfig_id *id)
+{
+	if (id) {
+		EC_POINT_clear_free(id->e_id);
+		EVP_PKEY_free(id->csign);
+		EVP_PKEY_free(id->a_nonce);
+		EVP_PKEY_free(id->e_prime_id);
+		EVP_PKEY_free(id->pp_key);
+		os_free(id);
+	}
+}
+
+
+EC_POINT * dpp_decrypt_e_id(EVP_PKEY *ppkey, EVP_PKEY *a_nonce,
+			    EVP_PKEY *e_prime_id)
+{
+	const EC_KEY *pp_ec, *a_nonce_ec, *e_prime_id_ec;
+	const BIGNUM *pp_bn;
+	const EC_GROUP *group;
+	EC_POINT *e_id = NULL;
+	const EC_POINT *a_nonce_point, *e_prime_id_point;
+	BN_CTX *ctx = NULL;
+
+	if (!ppkey)
+		return NULL;
+
+	/* E-id = E'-id - s_C * A-NONCE */
+	pp_ec = EVP_PKEY_get0_EC_KEY(ppkey);
+	a_nonce_ec = EVP_PKEY_get0_EC_KEY(a_nonce);
+	e_prime_id_ec = EVP_PKEY_get0_EC_KEY(e_prime_id);
+	if (!pp_ec || !a_nonce_ec || !e_prime_id_ec)
+		return NULL;
+	pp_bn = EC_KEY_get0_private_key(pp_ec);
+	group = EC_KEY_get0_group(pp_ec);
+	a_nonce_point = EC_KEY_get0_public_key(a_nonce_ec);
+	e_prime_id_point = EC_KEY_get0_public_key(e_prime_id_ec);
+	ctx = BN_CTX_new();
+	if (!pp_bn || !group || !a_nonce_point || !e_prime_id_point || !ctx)
+		goto fail;
+	e_id = EC_POINT_new(group);
+	if (!e_id ||
+	    !EC_POINT_mul(group, e_id, NULL, a_nonce_point, pp_bn, ctx) ||
+	    !EC_POINT_invert(group, e_id, ctx) ||
+	    !EC_POINT_add(group, e_id, e_prime_id_point, e_id, ctx)) {
+		EC_POINT_clear_free(e_id);
+		goto fail;
+	}
+
+	dpp_debug_print_point("DPP: Decrypted E-id", group, e_id);
+
+fail:
+	BN_CTX_free(ctx);
+	return e_id;
+}
+
 #endif /* CONFIG_DPP2 */
 
 
diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h
index e66eb6c..af12467 100644
--- a/src/common/dpp_i.h
+++ b/src/common/dpp_i.h
@@ -73,6 +73,7 @@
 const struct dpp_curve_params * dpp_get_curve_name(const char *name);
 const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name);
 const struct dpp_curve_params * dpp_get_curve_nid(int nid);
+const struct dpp_curve_params * dpp_get_curve_ike_group(u16 group);
 int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
 		       const u8 *data, size_t data_len);
 struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix);
@@ -132,11 +133,22 @@
 int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
 				     const u8 *r_proto, u16 r_proto_len,
 				     struct json_token *net_access_key);
+EC_POINT * dpp_decrypt_e_id(EVP_PKEY *ppkey, EVP_PKEY *a_nonce,
+			    EVP_PKEY *e_prime_id);
 char * dpp_sign_connector(struct dpp_configurator *conf,
 			  const struct wpabuf *dppcon);
 int dpp_test_gen_invalid_key(struct wpabuf *msg,
 			     const struct dpp_curve_params *curve);
 
+struct dpp_reconfig_id {
+	const EC_GROUP *group;
+	EC_POINT *e_id; /* E-id */
+	EVP_PKEY *csign;
+	EVP_PKEY *a_nonce; /* A-NONCE */
+	EVP_PKEY *e_prime_id; /* E'-id */
+	EVP_PKEY *pp_key;
+};
+
 /* dpp_tcp.c */
 
 void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
diff --git a/src/common/dpp_reconfig.c b/src/common/dpp_reconfig.c
index 0bb00cc..c4a0273 100644
--- a/src/common/dpp_reconfig.c
+++ b/src/common/dpp_reconfig.c
@@ -34,9 +34,12 @@
 
 
 struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
-						size_t csign_key_len)
+						size_t csign_key_len,
+						const u8 *net_access_key,
+						size_t net_access_key_len,
+						struct dpp_reconfig_id *id)
 {
-	struct wpabuf *msg;
+	struct wpabuf *msg = NULL;
 	EVP_PKEY *csign = NULL;
 	const unsigned char *p;
 	struct wpabuf *uncomp;
@@ -44,39 +47,86 @@
 	const u8 *addr[1];
 	size_t len[1];
 	int res;
+	size_t attr_len;
+	const struct dpp_curve_params *own_curve;
+	EVP_PKEY *own_key;
+	struct wpabuf *a_nonce = NULL, *e_id = NULL;
 
 	wpa_printf(MSG_DEBUG, "DPP: Build Reconfig Announcement frame");
 
+	own_key = dpp_set_keypair(&own_curve, net_access_key,
+				  net_access_key_len);
+	if (!own_key) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+		goto fail;
+	}
+
 	p = csign_key;
 	csign = d2i_PUBKEY(NULL, &p, csign_key_len);
 	if (!csign) {
 		wpa_printf(MSG_ERROR,
 			   "DPP: Failed to parse local C-sign-key information");
-		return NULL;
+		goto fail;
 	}
 
 	uncomp = dpp_get_pubkey_point(csign, 1);
 	EVP_PKEY_free(csign);
 	if (!uncomp)
-		return NULL;
+		goto fail;
 	addr[0] = wpabuf_head(uncomp);
 	len[0] = wpabuf_len(uncomp);
 	wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed C-sign key", addr[0], len[0]);
 	res = sha256_vector(1, addr, len, hash);
 	wpabuf_free(uncomp);
 	if (res < 0)
-		return NULL;
+		goto fail;
 	wpa_hexdump(MSG_DEBUG, "DPP: kid = SHA256(uncompressed C-sign key)",
 		    hash, SHA256_MAC_LEN);
 
-	msg = dpp_alloc_msg(DPP_PA_RECONFIG_ANNOUNCEMENT, 4 + SHA256_MAC_LEN);
+	if (dpp_update_reconfig_id(id) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate E'-id");
+		goto fail;
+	}
+
+	a_nonce = dpp_get_pubkey_point(id->a_nonce, 0);
+	e_id = dpp_get_pubkey_point(id->e_prime_id, 0);
+	if (!a_nonce || !e_id)
+		goto fail;
+
+	attr_len = 4 + SHA256_MAC_LEN;
+	attr_len += 4 + 2;
+	attr_len += 4 + wpabuf_len(a_nonce);
+	attr_len += 4 + wpabuf_len(e_id);
+	msg = dpp_alloc_msg(DPP_PA_RECONFIG_ANNOUNCEMENT, attr_len);
 	if (!msg)
-		return NULL;
+		goto fail;
 
 	/* Configurator C-sign key Hash */
 	dpp_build_attr_csign_key_hash(msg, hash);
+
+	/* Finite Cyclic Group attribute */
+	wpa_printf(MSG_DEBUG, "DPP: Finite Cyclic Group: %u",
+		   own_curve->ike_group);
+	wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+	wpabuf_put_le16(msg, 2);
+	wpabuf_put_le16(msg, own_curve->ike_group);
+
+	/* A-NONCE */
+	wpabuf_put_le16(msg, DPP_ATTR_A_NONCE);
+	wpabuf_put_le16(msg, wpabuf_len(a_nonce));
+	wpabuf_put_buf(msg, a_nonce);
+
+	/* E'-id */
+	wpabuf_put_le16(msg, DPP_ATTR_E_PRIME_ID);
+	wpabuf_put_le16(msg, wpabuf_len(e_id));
+	wpabuf_put_buf(msg, e_id);
+
 	wpa_hexdump_buf(MSG_DEBUG,
 			"DPP: Reconfig Announcement frame attributes", msg);
+fail:
+	wpabuf_free(a_nonce);
+	wpabuf_free(e_id);
+	EVP_PKEY_free(own_key);
 	return msg;
 }
 
@@ -108,10 +158,10 @@
 	wpabuf_put_le16(msg, os_strlen(auth->conf->connector));
 	wpabuf_put_str(msg, auth->conf->connector);
 
-	/* I-nonce */
-	wpabuf_put_le16(msg, DPP_ATTR_I_NONCE);
+	/* C-nonce */
+	wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE);
 	wpabuf_put_le16(msg, auth->curve->nonce_len);
-	wpabuf_put_data(msg, auth->i_nonce, auth->curve->nonce_len);
+	wpabuf_put_data(msg, auth->c_nonce, auth->curve->nonce_len);
 
 	wpa_hexdump_buf(MSG_DEBUG,
 			"DPP: Reconfig Authentication Request frame attributes",
@@ -121,7 +171,9 @@
 }
 
 
-static int dpp_configurator_build_own_connector(struct dpp_configurator *conf)
+static int
+dpp_configurator_build_own_connector(struct dpp_configurator *conf,
+				     const struct dpp_curve_params *curve)
 {
 	struct wpabuf *dppcon = NULL;
 	int ret = -1;
@@ -132,12 +184,12 @@
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Sign own Configurator Connector for reconfiguration with curve %s",
 		   conf->curve->name);
-	conf->connector_key = dpp_gen_keypair(conf->curve);
+	conf->connector_key = dpp_gen_keypair(curve);
 	if (!conf->connector_key)
 		goto fail;
 
 	/* Connector (JSON dppCon object) */
-	dppcon = wpabuf_alloc(1000 + 2 * conf->curve->prime_len * 4 / 3);
+	dppcon = wpabuf_alloc(1000 + 2 * curve->prime_len * 4 / 3);
 	if (!dppcon)
 		goto fail;
 	json_start_object(dppcon, NULL);
@@ -150,7 +202,7 @@
 	json_end_array(dppcon);
 	json_value_sep(dppcon);
 	if (dpp_build_jwk(dppcon, "netAccessKey", conf->connector_key, NULL,
-			  conf->curve) < 0) {
+			  curve) < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
 		goto fail;
 	}
@@ -172,9 +224,58 @@
 
 struct dpp_authentication *
 dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
-		  struct dpp_configurator *conf, unsigned int freq)
+		  struct dpp_configurator *conf, unsigned int freq, u16 group,
+		  const u8 *a_nonce_attr, size_t a_nonce_len,
+		  const u8 *e_id_attr, size_t e_id_len)
 {
 	struct dpp_authentication *auth;
+	const struct dpp_curve_params *curve;
+	EVP_PKEY *a_nonce, *e_prime_id;
+	EC_POINT *e_id;
+
+	curve = dpp_get_curve_ike_group(group);
+	if (!curve) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported group %u - cannot reconfigure",
+			   group);
+		return NULL;
+	}
+
+	if (!a_nonce_attr) {
+		wpa_printf(MSG_INFO, "DPP: Missing required A-NONCE attribute");
+		return NULL;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: A-NONCE", a_nonce_attr, a_nonce_len);
+	a_nonce = dpp_set_pubkey_point(conf->csign, a_nonce_attr, a_nonce_len);
+	if (!a_nonce) {
+		wpa_printf(MSG_INFO, "DPP: Invalid A-NONCE");
+		return NULL;
+	}
+	dpp_debug_print_key("A-NONCE", a_nonce);
+
+	if (!e_id_attr) {
+		wpa_printf(MSG_INFO, "DPP: Missing required E'-id attribute");
+		return NULL;
+	}
+	e_prime_id = dpp_set_pubkey_point(conf->csign, e_id_attr, e_id_len);
+	if (!e_prime_id) {
+		wpa_printf(MSG_INFO, "DPP: Invalid E'-id");
+		EVP_PKEY_free(a_nonce);
+		return NULL;
+	}
+	dpp_debug_print_key("E'-id", e_prime_id);
+	e_id = dpp_decrypt_e_id(conf->pp_key, a_nonce, e_prime_id);
+	EVP_PKEY_free(a_nonce);
+	EVP_PKEY_free(e_prime_id);
+	if (!e_id) {
+		wpa_printf(MSG_INFO, "DPP: Could not decrypt E'-id");
+		return NULL;
+	}
+	/* TODO: could use E-id to determine whether reconfiguration with this
+	 * Enrollee has already been started and is waiting for updated
+	 * configuration instead of replying again before such configuration
+	 * becomes available */
+	EC_POINT_clear_free(e_id);
 
 	auth = dpp_alloc_auth(dpp, msg_ctx);
 	if (!auth)
@@ -186,16 +287,16 @@
 	auth->waiting_auth_resp = 1;
 	auth->allowed_roles = DPP_CAPAB_CONFIGURATOR;
 	auth->configurator = 1;
-	auth->curve = conf->curve;
+	auth->curve = curve;
 	auth->transaction_id = 1;
 	if (freq && dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
 		goto fail;
 
-	if (dpp_configurator_build_own_connector(conf) < 0)
+	if (dpp_configurator_build_own_connector(conf, curve) < 0)
 		goto fail;
 
-	if (random_get_bytes(auth->i_nonce, auth->curve->nonce_len)) {
-		wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+	if (random_get_bytes(auth->c_nonce, auth->curve->nonce_len)) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to generate C-nonce");
 		goto fail;
 	}
 
@@ -224,21 +325,16 @@
 	int res = -1;
 
 	/* Build DPP Reconfig Authentication Response frame attributes */
-	clear_len = 2 * (4 + auth->curve->nonce_len) +
+	clear_len = 4 + auth->curve->nonce_len +
 		4 + wpabuf_len(conn_status);
 	clear = wpabuf_alloc(clear_len);
 	if (!clear)
 		goto fail;
 
-	/* I-nonce (wrapped) */
-	wpabuf_put_le16(clear, DPP_ATTR_I_NONCE);
+	/* C-nonce (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE);
 	wpabuf_put_le16(clear, auth->curve->nonce_len);
-	wpabuf_put_data(clear, auth->i_nonce, auth->curve->nonce_len);
-
-	/* R-nonce (wrapped) */
-	wpabuf_put_le16(clear, DPP_ATTR_R_NONCE);
-	wpabuf_put_le16(clear, auth->curve->nonce_len);
-	wpabuf_put_data(clear, auth->r_nonce, auth->curve->nonce_len);
+	wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len);
 
 	/* Connection Status (wrapped) */
 	wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS);
@@ -251,6 +347,7 @@
 
 	attr_len = 4 + 1 + 4 + 1 +
 		4 + os_strlen(own_connector) +
+		4 + auth->curve->nonce_len +
 		4 + wpabuf_len(pr) +
 		4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
 	msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_RESP, attr_len);
@@ -274,6 +371,11 @@
 	wpabuf_put_le16(msg, os_strlen(own_connector));
 	wpabuf_put_str(msg, own_connector);
 
+	/* E-nonce */
+	wpabuf_put_le16(msg, DPP_ATTR_ENROLLEE_NONCE);
+	wpabuf_put_le16(msg, auth->curve->nonce_len);
+	wpabuf_put_data(msg, auth->e_nonce, auth->curve->nonce_len);
+
 	/* Responder Protocol Key (Pr) */
 	wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
 	wpabuf_put_le16(msg, wpabuf_len(pr));
@@ -291,7 +393,7 @@
 	len[1] = attr_end - attr_start;
 	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
 
-	/* Wrapped Data: {I-nonce, R-nonce, Connection Status}ke */
+	/* Wrapped Data: {C-nonce, E-nonce, Connection Status}ke */
 	wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
 	wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
 	wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
@@ -329,8 +431,8 @@
 			 const u8 *attr_start, size_t attr_len)
 {
 	struct dpp_authentication *auth = NULL;
-	const u8 *trans_id, *version, *i_connector, *i_nonce;
-	u16 trans_id_len, version_len, i_connector_len, i_nonce_len;
+	const u8 *trans_id, *version, *i_connector, *c_nonce;
+	u16 trans_id_len, version_len, i_connector_len, c_nonce_len;
 	struct dpp_signed_connector_info info;
 	enum dpp_status_error res;
 	struct json_token *root = NULL, *own_root = NULL, *token;
@@ -364,14 +466,14 @@
 	wpa_hexdump_ascii(MSG_DEBUG, "DPP: I-Connector",
 			  i_connector, i_connector_len);
 
-	i_nonce = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_NONCE,
-			       &i_nonce_len);
-	if (!i_nonce || i_nonce_len > DPP_MAX_NONCE_LEN) {
+	c_nonce = dpp_get_attr(attr_start, attr_len,
+			       DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+	if (!c_nonce || c_nonce_len > DPP_MAX_NONCE_LEN) {
 		wpa_printf(MSG_DEBUG,
-			   "DPP: Missing or invalid I-Nonce attribute");
+			   "DPP: Missing or invalid C-nonce attribute");
 		goto fail;
 	}
-	wpa_hexdump(MSG_DEBUG, "DPP: I-Nonce", i_nonce, i_nonce_len);
+	wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
 
 	res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
 					 i_connector, i_connector_len);
@@ -418,26 +520,19 @@
 	wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
 		   auth->peer_version);
 
-	os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+	os_memcpy(auth->c_nonce, c_nonce, c_nonce_len);
 
 	if (dpp_reconfig_derive_ke_responder(auth, net_access_key,
 					     net_access_key_len, token) < 0)
 		goto fail;
 
-	if (i_nonce_len != auth->curve->nonce_len) {
+	if (c_nonce_len != auth->curve->nonce_len) {
 		wpa_printf(MSG_DEBUG,
-			   "DPP: Unexpected I-nonce length %u (curve nonce len %zu)",
-			   i_nonce_len, auth->curve->nonce_len);
+			   "DPP: Unexpected C-nonce length %u (curve nonce len %zu)",
+			   c_nonce_len, auth->curve->nonce_len);
 		goto fail;
 	}
 
-	if (random_get_bytes(auth->r_nonce, auth->curve->nonce_len)) {
-		wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
-		goto fail;
-	}
-	wpa_hexdump_key(MSG_DEBUG, "DPP: R-nonce",
-			auth->r_nonce, auth->curve->nonce_len);
-
 	/* Build Connection Status object */
 	/* TODO: Get appropriate result value */
 	/* TODO: ssid64 and channelList */
@@ -462,40 +557,19 @@
 }
 
 
-static struct wpabuf *
-dpp_build_reconfig_flags(enum dpp_connector_key connector_key)
-{
-	struct wpabuf *json;
-
-	json = wpabuf_alloc(100);
-	if (!json)
-		return NULL;
-	json_start_object(json, NULL);
-	json_add_int(json, "connectorKey", connector_key);
-	json_end_object(json);
-	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Reconfig-Flags JSON",
-			  wpabuf_head(json), wpabuf_len(json));
-
-	return json;
-}
-
-
 struct wpabuf *
 dpp_reconfig_build_conf(struct dpp_authentication *auth)
 {
-	struct wpabuf *msg = NULL, *clear = NULL, *reconfig_flags;
+	struct wpabuf *msg = NULL, *clear;
 	u8 *attr_start, *attr_end;
 	size_t clear_len, attr_len, len[2];
 	const u8 *addr[2];
 	u8 *wrapped;
-
-	reconfig_flags = dpp_build_reconfig_flags(DPP_CONFIG_REPLACEKEY);
-	if (!reconfig_flags)
-		goto fail;
+	u8 flags;
 
 	/* Build DPP Reconfig Authentication Confirm frame attributes */
 	clear_len = 4 + 1 + 4 + 1 + 2 * (4 + auth->curve->nonce_len) +
-		4 + wpabuf_len(reconfig_flags);
+		4 + 1;
 	clear = wpabuf_alloc(clear_len);
 	if (!clear)
 		goto fail;
@@ -510,27 +584,33 @@
 	wpabuf_put_le16(clear, 1);
 	wpabuf_put_u8(clear, auth->peer_version);
 
-	/* I-nonce (wrapped) */
-	wpabuf_put_le16(clear, DPP_ATTR_I_NONCE);
+	/* C-nonce (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE);
 	wpabuf_put_le16(clear, auth->curve->nonce_len);
-	wpabuf_put_data(clear, auth->i_nonce, auth->curve->nonce_len);
+	wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len);
 
-	/* R-nonce (wrapped) */
-	wpabuf_put_le16(clear, DPP_ATTR_R_NONCE);
+	/* E-nonce (wrapped) */
+	wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
 	wpabuf_put_le16(clear, auth->curve->nonce_len);
-	wpabuf_put_data(clear, auth->r_nonce, auth->curve->nonce_len);
+	wpabuf_put_data(clear, auth->e_nonce, auth->curve->nonce_len);
 
 	/* Reconfig-Flags (wrapped) */
+	flags = DPP_CONFIG_REPLACEKEY;
 	wpabuf_put_le16(clear, DPP_ATTR_RECONFIG_FLAGS);
-	wpabuf_put_le16(clear, wpabuf_len(reconfig_flags));
-	wpabuf_put_buf(clear, reconfig_flags);
+	wpabuf_put_le16(clear, 1);
+	wpabuf_put_u8(clear, flags);
 
 	attr_len = 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
+	attr_len += 4 + 1;
 	msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_CONF, attr_len);
 	if (!msg)
 		goto fail;
 
 	attr_start = wpabuf_put(msg, 0);
+
+	/* DPP Status */
+	dpp_build_attr_status(msg, DPP_STATUS_OK);
+
 	attr_end = wpabuf_put(msg, 0);
 
 	/* OUI, OUI type, Crypto Suite, DPP frame type */
@@ -559,7 +639,6 @@
 			msg);
 
 out:
-	wpabuf_free(reconfig_flags);
 	wpabuf_free(clear);
 	return msg;
 fail:
@@ -574,9 +653,9 @@
 			 const u8 *attr_start, size_t attr_len)
 {
 	const u8 *trans_id, *version, *r_connector, *r_proto, *wrapped_data,
-		*i_nonce, *r_nonce, *conn_status;
+		*c_nonce, *e_nonce, *conn_status;
 	u16 trans_id_len, version_len, r_connector_len, r_proto_len,
-		wrapped_data_len, i_nonce_len, r_nonce_len, conn_status_len;
+		wrapped_data_len, c_nonce_len, e_nonce_len, conn_status_len;
 	struct wpabuf *conf = NULL;
 	char *signed_connector = NULL;
 	struct dpp_signed_connector_info info;
@@ -634,6 +713,15 @@
 	wpa_hexdump_ascii(MSG_DEBUG, "DPP: R-Connector",
 			  r_connector, r_connector_len);
 
+	e_nonce = dpp_get_attr(attr_start, attr_len,
+			       DPP_ATTR_ENROLLEE_NONCE, &e_nonce_len);
+	if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+		dpp_auth_fail(auth, "Missing or invalid E-nonce");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len);
+	os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
+
 	r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
 			       &r_proto_len);
 	if (!r_proto) {
@@ -702,23 +790,14 @@
 		goto fail;
 	}
 
-	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
-			       &i_nonce_len);
-	if (!i_nonce || i_nonce_len != auth->curve->nonce_len ||
-	    os_memcmp(i_nonce, auth->i_nonce, i_nonce_len) != 0) {
-		dpp_auth_fail(auth, "Missing or invalid I-nonce");
+	c_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+	if (!c_nonce || c_nonce_len != auth->curve->nonce_len ||
+	    os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Missing or invalid C-nonce");
 		goto fail;
 	}
-	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
-
-	r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
-			       &r_nonce_len);
-	if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
-		dpp_auth_fail(auth, "Missing or invalid R-nonce");
-		goto fail;
-	}
-	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
-	os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+	wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
 
 	conn_status = dpp_get_attr(unwrapped, unwrapped_len,
 				   DPP_ATTR_CONN_STATUS, &conn_status_len);
@@ -758,16 +837,16 @@
 int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
 			      const u8 *attr_start, size_t attr_len)
 {
-	const u8 *trans_id, *version, *wrapped_data, *i_nonce, *r_nonce,
-		*reconfig_flags;
-	u16 trans_id_len, version_len, wrapped_data_len, i_nonce_len,
-		r_nonce_len, reconfig_flags_len;
+	const u8 *trans_id, *version, *wrapped_data, *c_nonce, *e_nonce,
+		*reconfig_flags, *status;
+	u16 trans_id_len, version_len, wrapped_data_len, c_nonce_len,
+		e_nonce_len, reconfig_flags_len, status_len;
 	const u8 *addr[2];
 	size_t len[2];
 	u8 *unwrapped = NULL;
 	size_t unwrapped_len = 0;
-	struct json_token *root = NULL, *token;
 	int res = -1;
+	u8 flags;
 
 	if (!auth->reconfig || auth->configurator)
 		goto fail;
@@ -781,11 +860,26 @@
 	}
 	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
 		    wrapped_data, wrapped_data_len);
+	attr_len = wrapped_data - 4 - attr_start;
+
+	status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+			      &status_len);
+	if (!status || status_len < 1) {
+		dpp_auth_fail(auth,
+			      "Missing or invalid required DPP Status attribute");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+	if (status[0] != DPP_STATUS_OK) {
+		dpp_auth_fail(auth,
+			      "Reconfiguration did not complete successfully");
+		goto fail;
+	}
 
 	addr[0] = hdr;
 	len[0] = DPP_HDR_LEN;
 	addr[1] = attr_start;
-	len[1] = 0;
+	len[1] = attr_len;
 	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
 	wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
 	wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
@@ -825,55 +919,38 @@
 		goto fail;
 	}
 
-	i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
-			       &i_nonce_len);
-	if (!i_nonce || i_nonce_len != auth->curve->nonce_len ||
-	    os_memcmp(i_nonce, auth->i_nonce, i_nonce_len) != 0) {
-		dpp_auth_fail(auth, "Missing or invalid I-nonce");
+	c_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+	if (!c_nonce || c_nonce_len != auth->curve->nonce_len ||
+	    os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Missing or invalid C-nonce");
 		goto fail;
 	}
-	wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+	wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
 
-	r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
-			       &r_nonce_len);
-	if (!r_nonce || r_nonce_len != auth->curve->nonce_len ||
-	    os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
-		dpp_auth_fail(auth, "Missing or invalid R-nonce");
+	e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+			       DPP_ATTR_ENROLLEE_NONCE, &e_nonce_len);
+	if (!e_nonce || e_nonce_len != auth->curve->nonce_len ||
+	    os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
+		dpp_auth_fail(auth, "Missing or invalid E-nonce");
 		goto fail;
 	}
-	wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+	wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len);
 
 	reconfig_flags = dpp_get_attr(unwrapped, unwrapped_len,
 				      DPP_ATTR_RECONFIG_FLAGS,
 				      &reconfig_flags_len);
-	if (!reconfig_flags) {
+	if (!reconfig_flags || reconfig_flags_len < 1) {
 		dpp_auth_fail(auth, "Missing or invalid Reconfig-Flags");
 		goto fail;
 	}
-	wpa_hexdump_ascii(MSG_DEBUG, "DPP: Reconfig-Flags",
-			  reconfig_flags, reconfig_flags_len);
-	root = json_parse((const char *) reconfig_flags, reconfig_flags_len);
-	if (!root) {
-		dpp_auth_fail(auth, "Could not parse Reconfig-Flags");
-		goto fail;
-	}
-	token = json_get_member(root, "connectorKey");
-	if (!token || token->type != JSON_NUMBER) {
-		dpp_auth_fail(auth, "No connectorKey in Reconfig-Flags");
-		goto fail;
-	}
-	if (token->number != DPP_CONFIG_REUSEKEY &&
-	    token->number != DPP_CONFIG_REPLACEKEY) {
-		dpp_auth_fail(auth,
-			      "Unsupported connectorKey value in Reconfig-Flags");
-		goto fail;
-	}
-	auth->reconfig_connector_key = token->number;
+	flags = reconfig_flags[0] & BIT(0);
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Flags connectorKey=%u", flags);
+	auth->reconfig_connector_key = flags;
 
 	auth->reconfig_success = true;
 	res = 0;
 fail:
-	json_free(root);
 	bin_clear_free(unwrapped, unwrapped_len);
 	return res;
 }
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
index fc53b8a..7e330d6 100644
--- a/src/common/dpp_tcp.c
+++ b/src/common/dpp_tcp.c
@@ -25,6 +25,9 @@
 	struct dpp_relay_controller *relay;
 	struct dpp_global *global;
 	struct dpp_authentication *auth;
+	void *msg_ctx;
+	void *cb_ctx;
+	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
 	int sock;
 	u8 mac_addr[ETH_ALEN];
 	unsigned int freq;
@@ -38,6 +41,10 @@
 	unsigned int on_tcp_tx_complete_gas_done:1;
 	unsigned int on_tcp_tx_complete_remove:1;
 	unsigned int on_tcp_tx_complete_auth_ok:1;
+	unsigned int gas_comeback_in_progress:1;
+	u8 gas_dialog_token;
+	char *name;
+	enum dpp_netrole netrole;
 };
 
 /* Remote Controller */
@@ -46,6 +53,7 @@
 	struct dpp_global *global;
 	u8 pkhash[SHA256_MAC_LEN];
 	struct hostapd_ip_addr ipaddr;
+	void *msg_ctx;
 	void *cb_ctx;
 	void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
 		   size_t len);
@@ -62,12 +70,18 @@
 	int sock;
 	struct dl_list conn; /* struct dpp_connection */
 	char *configurator_params;
+	enum dpp_netrole netrole;
+	void *msg_ctx;
+	void *cb_ctx;
+	int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
 };
 
 static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
 static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
 static void dpp_controller_auth_success(struct dpp_connection *conn,
 					int initiator);
+static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx);
+static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx);
 
 
 static void dpp_connection_free(struct dpp_connection *conn)
@@ -81,9 +95,12 @@
 	}
 	eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
 			     conn, NULL);
+	eloop_cancel_timeout(dpp_tcp_build_csr, conn, NULL);
+	eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
 	wpabuf_free(conn->msg);
 	wpabuf_free(conn->msg_out);
 	dpp_auth_deinit(conn->auth);
+	os_free(conn->name);
 	os_free(conn);
 }
 
@@ -110,6 +127,7 @@
 	ctrl->global = dpp;
 	os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
 	os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
+	ctrl->msg_ctx = config->msg_ctx;
 	ctrl->cb_ctx = config->cb_ctx;
 	ctrl->tx = config->tx;
 	ctrl->gas_resp_tx = config->gas_resp_tx;
@@ -139,7 +157,12 @@
 static void dpp_controller_gas_done(struct dpp_connection *conn)
 {
 	struct dpp_authentication *auth = conn->auth;
-	void *msg_ctx;
+
+	if (auth->waiting_csr) {
+		wpa_printf(MSG_DEBUG, "DPP: Waiting for CSR");
+		conn->on_tcp_tx_complete_gas_done = 0;
+		return;
+	}
 
 	if (auth->peer_version >= 2 &&
 	    auth->conf_resp_status == DPP_STATUS_OK) {
@@ -148,11 +171,7 @@
 		return;
 	}
 
-	if (conn->ctrl)
-		msg_ctx = conn->ctrl->global->msg_ctx;
-	else
-		msg_ctx = auth->msg_ctx;
-	wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+	wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
 	dpp_connection_remove(conn);
 }
 
@@ -243,9 +262,11 @@
 {
 	struct dpp_authentication *auth = conn->auth;
 	struct wpabuf *buf;
-	int netrole_ap = 0; /* TODO: make this configurable */
+	const char *dpp_name;
 
-	buf = dpp_build_conf_req_helper(auth, "Test", netrole_ap, NULL, NULL);
+	dpp_name = conn->name ? conn->name : "Test";
+	buf = dpp_build_conf_req_helper(auth, dpp_name, conn->netrole, NULL,
+					NULL);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No configuration request data available");
@@ -266,7 +287,7 @@
 		return;
 
 	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
-	wpa_msg(conn->global->msg_ctx, MSG_INFO,
+	wpa_msg(conn->msg_ctx, MSG_INFO,
 		DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
@@ -356,6 +377,8 @@
 
 	conn->global = ctrl->global;
 	conn->relay = ctrl;
+	conn->msg_ctx = ctrl->msg_ctx;
+	conn->cb_ctx = ctrl->global->cb_ctx;
 	os_memcpy(conn->mac_addr, src, ETH_ALEN);
 	conn->freq = freq;
 
@@ -608,8 +631,7 @@
 		return 0;
 	}
 
-	conn->auth = dpp_auth_req_rx(conn->ctrl->global,
-				     conn->ctrl->global->msg_ctx,
+	conn->auth = dpp_auth_req_rx(conn->ctrl->global, conn->msg_ctx,
 				     conn->ctrl->allowed_roles,
 				     conn->ctrl->qr_mutual,
 				     peer_bi, own_bi, -1, hdr, buf, len);
@@ -645,7 +667,7 @@
 		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: Start wait for full response");
-			return -1;
+			return 0;
 		}
 		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
 		dpp_connection_remove(conn);
@@ -692,7 +714,7 @@
 
 	wpa_printf(MSG_DEBUG,
 		   "DPP: Timeout while waiting for Connection Status Result");
-	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
+	wpa_msg(conn->msg_ctx, MSG_INFO,
 		DPP_EVENT_CONN_STATUS_RESULT "timeout");
 	dpp_connection_remove(conn);
 }
@@ -704,7 +726,7 @@
 {
 	struct dpp_authentication *auth = conn->auth;
 	enum dpp_status_error status;
-	void *msg_ctx;
+	void *msg_ctx = conn->msg_ctx;
 
 	if (!conn->ctrl && (!auth || !auth->configurator))
 		return 0;
@@ -716,10 +738,6 @@
 			   "DPP: No DPP Configuration waiting for result - drop");
 		return -1;
 	}
-	if (conn->ctrl)
-		msg_ctx = conn->ctrl->global->msg_ctx;
-	else
-		msg_ctx = auth->msg_ctx;
 
 	status = dpp_conf_result_rx(auth, hdr, buf, len);
 	if (status == DPP_STATUS_OK && auth->send_conn_status) {
@@ -765,8 +783,7 @@
 
 	status = dpp_conn_status_result_rx(auth, hdr, buf, len,
 					   ssid, &ssid_len, &channel_list);
-	wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
-		DPP_EVENT_CONN_STATUS_RESULT
+	wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT
 		"result=%d ssid=%s channel_list=%s",
 		status, wpa_ssid_txt(ssid, ssid_len),
 		channel_list ? channel_list : "N/A");
@@ -796,7 +813,7 @@
 	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
 				   &r_bootstrap_len);
 	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
-		wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+		wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
 			"Missing or invalid required Responder Bootstrapping Key Hash attribute");
 		return -1;
 	}
@@ -809,7 +826,7 @@
 		return -1;
 	}
 
-	auth = dpp_auth_init(dpp, dpp->msg_ctx, peer_bi, NULL,
+	auth = dpp_auth_init(dpp, conn->msg_ctx, peer_bi, NULL,
 			     DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
 	if (!auth)
 		return -1;
@@ -828,11 +845,12 @@
 						   const u8 *hdr, const u8 *buf,
 						   size_t len)
 {
-	const u8 *csign_hash;
-	u16 csign_hash_len;
+	const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
+	u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
 	struct dpp_configurator *conf;
 	struct dpp_global *dpp = conn->ctrl->global;
 	struct dpp_authentication *auth;
+	u16 group;
 
 	if (conn->auth) {
 		wpa_printf(MSG_DEBUG,
@@ -845,7 +863,7 @@
 	csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
 				  &csign_hash_len);
 	if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
-		wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+		wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
 			"Missing or invalid required Configurator C-sign key Hash attribute");
 		return -1;
 	}
@@ -858,7 +876,21 @@
 		return -1;
 	}
 
-	auth = dpp_reconfig_init(dpp, dpp->msg_ctx, conf, 0);
+	fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+			       &fcgroup_len);
+	if (!fcgroup || fcgroup_len != 2) {
+		wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Finite Cyclic Group attribute");
+		return -1;
+	}
+	group = WPA_GET_LE16(fcgroup);
+	wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
+
+	a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
+	e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
+
+	auth = dpp_reconfig_init(dpp, conn->msg_ctx, conf, 0, group,
+				 a_nonce, a_nonce_len, e_id, e_id_len);
 	if (!auth)
 		return -1;
 	if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
@@ -965,14 +997,91 @@
 }
 
 
+static int dpp_tcp_send_comeback_delay(struct dpp_connection *conn, u8 action)
+{
+	struct wpabuf *buf;
+	size_t len = 18;
+
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		len++;
+
+	buf = wpabuf_alloc(4 + len);
+	if (!buf)
+		return -1;
+
+	wpabuf_put_be32(buf, len);
+
+	wpabuf_put_u8(buf, action);
+	wpabuf_put_u8(buf, conn->gas_dialog_token);
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		wpabuf_put_u8(buf, 0);
+	wpabuf_put_le16(buf, 500); /* GAS Comeback Delay */
+
+	dpp_write_adv_proto(buf);
+	wpabuf_put_le16(buf, 0); /* Query Response Length */
+
+	/* Send Config Response over TCP */
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = buf;
+	dpp_tcp_send(conn);
+	return 0;
+}
+
+
+static int dpp_tcp_send_gas_resp(struct dpp_connection *conn, u8 action,
+				 struct wpabuf *resp)
+{
+	struct wpabuf *buf;
+	size_t len;
+
+	if (!resp)
+		return -1;
+
+	len = 18 + wpabuf_len(resp);
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		len++;
+
+	buf = wpabuf_alloc(4 + len);
+	if (!buf) {
+		wpabuf_free(resp);
+		return -1;
+	}
+
+	wpabuf_put_be32(buf, len);
+
+	wpabuf_put_u8(buf, action);
+	wpabuf_put_u8(buf, conn->gas_dialog_token);
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		wpabuf_put_u8(buf, 0);
+	wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
+
+	dpp_write_adv_proto(buf);
+	dpp_write_gas_query(buf, resp);
+	wpabuf_free(resp);
+
+	/* Send Config Response over TCP; GAS fragmentation is taken care of by
+	 * the Relay */
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = buf;
+	conn->on_tcp_tx_complete_gas_done = 1;
+	dpp_tcp_send(conn);
+	return 0;
+}
+
+
 static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
 				     size_t len)
 {
 	const u8 *pos, *end, *next;
-	u8 dialog_token;
 	const u8 *adv_proto;
 	u16 slen;
-	struct wpabuf *resp, *buf;
+	struct wpabuf *resp;
 	struct dpp_authentication *auth = conn->auth;
 
 	if (len < 1 + 2)
@@ -990,7 +1099,7 @@
 	pos = msg;
 	end = msg + len;
 
-	dialog_token = *pos++;
+	conn->gas_dialog_token = *pos++;
 	adv_proto = pos++;
 	slen = *pos++;
 	if (*adv_proto != WLAN_EID_ADV_PROTO ||
@@ -1015,35 +1124,76 @@
 		return -1;
 
 	resp = dpp_conf_req_rx(auth, pos, slen);
-	if (!resp)
+	if (!resp && auth->waiting_cert) {
+		wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
+		conn->gas_comeback_in_progress = 1;
+		return dpp_tcp_send_comeback_delay(conn,
+						   WLAN_PA_GAS_INITIAL_RESP);
+	}
+
+	return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_INITIAL_RESP, resp);
+}
+
+
+static int dpp_controller_rx_gas_comeback_req(struct dpp_connection *conn,
+					      const u8 *msg, size_t len)
+{
+	u8 dialog_token;
+	struct dpp_authentication *auth = conn->auth;
+	struct wpabuf *resp;
+
+	if (len < 1)
 		return -1;
 
-	buf = wpabuf_alloc(4 + 18 + wpabuf_len(resp));
-	if (!buf) {
-		wpabuf_free(resp);
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Configuration Request over TCP (comeback)");
+
+	if (!auth || (!conn->ctrl && !auth->configurator) ||
+	    (!auth->auth_success && !auth->reconfig_success) ||
+	    !conn->gas_comeback_in_progress) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
 		return -1;
 	}
 
-	wpabuf_put_be32(buf, 18 + wpabuf_len(resp));
+	dialog_token = msg[0];
+	if (dialog_token != conn->gas_dialog_token) {
+		wpa_printf(MSG_DEBUG, "DPP: Dialog token mismatch (%u != %u)",
+			   dialog_token, conn->gas_dialog_token);
+		return -1;
+	}
 
-	wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
-	wpabuf_put_u8(buf, dialog_token);
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-	wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
+	if (!auth->conf_resp_tcp) {
+		wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
+		return dpp_tcp_send_comeback_delay(conn,
+						   WLAN_PA_GAS_COMEBACK_RESP);
+	}
 
-	dpp_write_adv_proto(buf);
-	dpp_write_gas_query(buf, resp);
-	wpabuf_free(resp);
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Configuration response is ready to be sent out");
+	resp = auth->conf_resp_tcp;
+	auth->conf_resp_tcp = NULL;
+	return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_COMEBACK_RESP, resp);
+}
 
-	/* Send Config Response over TCP; GAS fragmentation is taken care of by
-	 * the Relay */
-	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
-	wpabuf_free(conn->msg_out);
-	conn->msg_out_pos = 0;
-	conn->msg_out = buf;
-	conn->on_tcp_tx_complete_gas_done = 1;
-	dpp_tcp_send(conn);
-	return 0;
+
+static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+	struct dpp_authentication *auth = conn->auth;
+
+	if (!auth || !auth->csrattrs)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build CSR");
+	wpabuf_free(auth->csr);
+	/* TODO: Additional information needed for CSR based on csrAttrs */
+	auth->csr = dpp_build_csr(auth, conn->name ? conn->name : "Test");
+	if (!auth->csr) {
+		dpp_connection_remove(conn);
+		return;
+	}
+
+	dpp_controller_start_gas_client(conn);
 }
 
 
@@ -1062,14 +1212,18 @@
 	else
 		res = -1;
 	wpabuf_free(resp);
+	if (res == -2) {
+		wpa_printf(MSG_DEBUG, "DPP: CSR needed");
+		eloop_register_timeout(0, 0, dpp_tcp_build_csr, conn, NULL);
+		return 0;
+	}
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
 		return -1;
 	}
 
-	if (conn->global->process_conf_obj)
-		res = conn->global->process_conf_obj(conn->global->cb_ctx,
-						     auth);
+	if (conn->process_conf_obj)
+		res = conn->process_conf_obj(conn->cb_ctx, auth);
 	else
 		res = 0;
 
@@ -1092,15 +1246,40 @@
 }
 
 
+static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx)
+{
+	struct dpp_connection *conn = eloop_ctx;
+	struct dpp_authentication *auth = conn->auth;
+	struct wpabuf *msg;
+
+	if (!auth)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Send GAS Comeback Request");
+	msg = wpabuf_alloc(4 + 2);
+	if (!msg)
+		return;
+	wpabuf_put_be32(msg, 2);
+	wpabuf_put_u8(msg, WLAN_PA_GAS_COMEBACK_REQ);
+	wpabuf_put_u8(msg, conn->gas_dialog_token);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+
+	wpabuf_free(conn->msg_out);
+	conn->msg_out_pos = 0;
+	conn->msg_out = msg;
+	dpp_tcp_send(conn);
+}
+
+
 static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
-			   size_t len)
+			   size_t len, bool comeback)
 {
 	struct wpabuf *buf;
 	u8 dialog_token;
 	const u8 *pos, *end, *next, *adv_proto;
-	u16 status, slen;
+	u16 status, slen, comeback_delay;
 
-	if (len < 5 + 2)
+	if (len < 5 + 2 + (comeback ? 1 : 0))
 		return -1;
 
 	wpa_printf(MSG_DEBUG,
@@ -1116,7 +1295,10 @@
 		return -1;
 	}
 	pos += 2;
-	pos += 2; /* ignore GAS Comeback Delay */
+	if (comeback)
+		pos++; /* ignore Fragment ID */
+	comeback_delay = WPA_GET_LE16(pos);
+	pos += 2;
 
 	adv_proto = pos++;
 	slen = *pos++;
@@ -1141,6 +1323,20 @@
 	if (slen > end - pos)
 		return -1;
 
+	if (comeback_delay) {
+		unsigned int secs, usecs;
+
+		conn->gas_dialog_token = dialog_token;
+		secs = (comeback_delay * 1024) / 1000000;
+		usecs = comeback_delay * 1024 - secs * 1000000;
+		wpa_printf(MSG_DEBUG, "DPP: Comeback delay: %u",
+			   comeback_delay);
+		eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
+		eloop_register_timeout(secs, usecs, dpp_tcp_gas_query_comeback,
+				       conn, NULL);
+		return 0;
+	}
+
 	buf = wpabuf_alloc(slen);
 	if (!buf)
 		return -1;
@@ -1264,8 +1460,15 @@
 			dpp_connection_remove(conn);
 		break;
 	case WLAN_PA_GAS_INITIAL_RESP:
+	case WLAN_PA_GAS_COMEBACK_RESP:
 		if (dpp_rx_gas_resp(conn, pos + 1,
-				    wpabuf_len(conn->msg) - 1) < 0)
+				    wpabuf_len(conn->msg) - 1,
+				    *pos == WLAN_PA_GAS_COMEBACK_RESP) < 0)
+			dpp_connection_remove(conn);
+		break;
+	case WLAN_PA_GAS_COMEBACK_REQ:
+		if (dpp_controller_rx_gas_comeback_req(
+			    conn, pos + 1, wpabuf_len(conn->msg) - 1) < 0)
 			dpp_connection_remove(conn);
 		break;
 	default:
@@ -1302,7 +1505,11 @@
 
 	conn->global = ctrl->global;
 	conn->ctrl = ctrl;
+	conn->msg_ctx = ctrl->msg_ctx;
+	conn->cb_ctx = ctrl->cb_ctx;
+	conn->process_conf_obj = ctrl->process_conf_obj;
 	conn->sock = fd;
+	conn->netrole = ctrl->netrole;
 
 	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
 		wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
@@ -1327,7 +1534,10 @@
 
 
 int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
-		 const struct hostapd_ip_addr *addr, int port)
+		 const struct hostapd_ip_addr *addr, int port, const char *name,
+		 enum dpp_netrole netrole, void *msg_ctx, void *cb_ctx,
+		 int (*process_conf_obj)(void *ctx,
+					 struct dpp_authentication *auth))
 {
 	struct dpp_connection *conn;
 	struct sockaddr_storage saddr;
@@ -1349,6 +1559,11 @@
 		return -1;
 	}
 
+	conn->msg_ctx = msg_ctx;
+	conn->cb_ctx = cb_ctx;
+	conn->process_conf_obj = process_conf_obj;
+	conn->name = os_strdup(name ? name : "Test");
+	conn->netrole = netrole;
 	conn->global = dpp;
 	conn->auth = auth;
 	conn->sock = socket(AF_INET, SOCK_STREAM, 0);
@@ -1418,7 +1633,11 @@
 			os_strdup(config->configurator_params);
 	dl_list_init(&ctrl->conn);
 	ctrl->allowed_roles = config->allowed_roles;
-	ctrl->qr_mutual = 0;
+	ctrl->qr_mutual = config->qr_mutual;
+	ctrl->netrole = config->netrole;
+	ctrl->msg_ctx = config->msg_ctx;
+	ctrl->cb_ctx = config->cb_ctx;
+	ctrl->process_conf_obj = config->process_conf_obj;
 
 	ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
 	if (ctrl->sock < 0)
@@ -1474,6 +1693,69 @@
 }
 
 
+static bool dpp_tcp_peer_id_match(struct dpp_authentication *auth,
+				  unsigned int id)
+{
+	return auth &&
+		((auth->peer_bi && auth->peer_bi->id == id) ||
+		 (auth->tmp_peer_bi && auth->tmp_peer_bi->id == id));
+}
+
+
+static struct dpp_authentication * dpp_tcp_get_auth(struct dpp_global *dpp,
+						    unsigned int id)
+{
+	struct dpp_connection *conn;
+
+	dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) {
+		if (dpp_tcp_peer_id_match(conn->auth, id))
+			return conn->auth;
+	}
+
+	return NULL;
+}
+
+
+struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp,
+						    unsigned int id)
+{
+	struct dpp_controller *ctrl = dpp->controller;
+	struct dpp_connection *conn;
+
+	if (!ctrl)
+		return dpp_tcp_get_auth(dpp, id);
+
+	dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+		if (dpp_tcp_peer_id_match(conn->auth, id))
+			return conn->auth;
+	}
+
+	return dpp_tcp_get_auth(dpp, id);
+}
+
+
+void dpp_controller_new_qr_code(struct dpp_global *dpp,
+				struct dpp_bootstrap_info *bi)
+{
+	struct dpp_controller *ctrl = dpp->controller;
+	struct dpp_connection *conn;
+
+	if (!ctrl)
+		return;
+
+	dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+		struct dpp_authentication *auth = conn->auth;
+
+		if (!auth->response_pending ||
+		    dpp_notify_new_qr_code(auth, bi) != 1)
+			continue;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Sending out pending authentication response");
+		dpp_tcp_send_msg(conn, conn->auth->resp_msg);
+	}
+}
+
+
 void dpp_tcp_init_flush(struct dpp_global *dpp)
 {
 	struct dpp_connection *conn, *tmp;
diff --git a/src/common/gas_server.c b/src/common/gas_server.c
index ca46758..c000aeb 100644
--- a/src/common/gas_server.c
+++ b/src/common/gas_server.c
@@ -1,6 +1,7 @@
 /*
  * Generic advertisement service (GAS) server
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2020, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -23,8 +24,9 @@
 	struct dl_list list;
 	u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
 	u8 adv_proto_id_len;
-	struct wpabuf * (*req_cb)(void *ctx, const u8 *sa,
-				  const u8 *query, size_t query_len);
+	struct wpabuf * (*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
+				  const u8 *query, size_t query_len,
+				  u16 *comeback_delay);
 	void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
 	void *ctx;
 	struct gas_server *gas;
@@ -39,6 +41,7 @@
 	u8 dst[ETH_ALEN];
 	u8 dialog_token;
 	struct gas_server_handler *handler;
+	u16 comeback_delay;
 };
 
 struct gas_server {
@@ -61,7 +64,8 @@
 		   response, MAC2STR(response->dst), response->dialog_token,
 		   response->freq, response->frag_id,
 		   (unsigned long) response->offset,
-		   (unsigned long) wpabuf_len(response->resp));
+		   (unsigned long) (response->resp ?
+				    wpabuf_len(response->resp) : 0));
 	response->handler->status_cb(response->handler->ctx,
 				     response->resp, 0);
 	response->resp = NULL;
@@ -83,30 +87,29 @@
 
 static void
 gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler,
+		     struct gas_server_response *response,
 		     const u8 *da, int freq, u8 dialog_token,
-		     struct wpabuf *query_resp)
+		     struct wpabuf *query_resp, u16 comeback_delay)
 {
 	size_t max_len = (freq > 56160) ? 928 : 1400;
 	size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
 	size_t resp_frag_len;
 	struct wpabuf *resp;
-	u16 comeback_delay;
-	struct gas_server_response *response;
 
-	if (!query_resp)
-		return;
-
-	response = os_zalloc(sizeof(*response));
-	if (!response) {
-		wpabuf_free(query_resp);
+	if (comeback_delay == 0 && !query_resp) {
+		gas_server_free_response(response);
 		return;
 	}
-	wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
+
 	response->freq = freq;
 	response->handler = handler;
 	os_memcpy(response->dst, da, ETH_ALEN);
 	response->dialog_token = dialog_token;
-	if (hdr_len + wpabuf_len(query_resp) > max_len) {
+	if (comeback_delay) {
+		/* Need more time to prepare the response */
+		resp_frag_len = 0;
+		response->comeback_delay = comeback_delay;
+	} else if (hdr_len + wpabuf_len(query_resp) > max_len) {
 		/* Need to use comeback to initiate fragmentation */
 		comeback_delay = 1;
 		resp_frag_len = 0;
@@ -135,10 +138,12 @@
 
 	/* Query Response Length */
 	wpabuf_put_le16(resp, resp_frag_len);
-	if (!comeback_delay)
+	if (!comeback_delay && query_resp)
 		wpabuf_put_buf(resp, query_resp);
 
-	if (comeback_delay) {
+	if (comeback_delay && !query_resp) {
+		wpa_printf(MSG_DEBUG, "GAS: No response available yet");
+	} else if (comeback_delay) {
 		wpa_printf(MSG_DEBUG,
 			   "GAS: Need to fragment query response");
 	} else {
@@ -165,6 +170,7 @@
 	u16 query_req_len;
 	struct gas_server_handler *handler;
 	struct wpabuf *resp;
+	struct gas_server_response *response;
 
 	wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
 		    data, len);
@@ -210,8 +216,15 @@
 			    pos, end - pos);
 	}
 
+	response = os_zalloc(sizeof(*response));
+	if (!response)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
 	dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
 			 list) {
+		u16 comeback_delay = 0;
+
 		if (adv_proto_len < 1 + handler->adv_proto_id_len ||
 		    os_memcmp(adv_proto + 1, handler->adv_proto_id,
 			      handler->adv_proto_id_len) != 0)
@@ -219,17 +232,22 @@
 
 		wpa_printf(MSG_DEBUG,
 			   "GAS: Calling handler for the requested Advertisement Protocol ID");
-		resp = handler->req_cb(handler->ctx, sa, query_req,
-				       query_req_len);
+		resp = handler->req_cb(handler->ctx, response, sa, query_req,
+				       query_req_len, &comeback_delay);
 		wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
 				resp);
-		gas_server_send_resp(gas, handler, sa, freq, dialog_token,
-				     resp);
+		if (comeback_delay)
+			wpa_printf(MSG_DEBUG,
+				   "GAS: Handler requested comeback delay: %u TU",
+				   comeback_delay);
+		gas_server_send_resp(gas, handler, response, sa, freq,
+				     dialog_token, resp, comeback_delay);
 		return 0;
 	}
 
 	wpa_printf(MSG_DEBUG,
 		   "GAS: No registered handler for the requested Advertisement Protocol ID");
+	gas_server_free_response(response);
 	return -1;
 }
 
@@ -243,6 +261,31 @@
 	size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
 	size_t remaining, resp_frag_len;
 	struct wpabuf *resp;
+	unsigned int wait_time = 0;
+
+	if (!response->resp) {
+		resp = gas_build_comeback_resp(response->dialog_token,
+					       WLAN_STATUS_SUCCESS, 0, 0,
+					       response->comeback_delay,
+					       handler->adv_proto_id_len);
+		if (!resp) {
+			dl_list_del(&response->list);
+			gas_server_free_response(response);
+			return;
+		}
+
+		/* Advertisement Protocol element */
+		wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
+		wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
+		wpabuf_put_u8(resp, 0x7f);
+		/* Advertisement Protocol ID */
+		wpabuf_put_data(resp, handler->adv_proto_id,
+				handler->adv_proto_id_len);
+
+		/* Query Response Length */
+		wpabuf_put_le16(resp, 0);
+		goto send_resp;
+	}
 
 	remaining = wpabuf_len(response->resp) - response->offset;
 	if (hdr_len + remaining > max_len)
@@ -279,8 +322,11 @@
 
 	response->offset += resp_frag_len;
 
-	gas->tx(gas->ctx, response->freq, response->dst, resp,
-		remaining > resp_frag_len ? 2000 : 0);
+	if (remaining > resp_frag_len)
+		wait_time = 2000;
+
+send_resp:
+	gas->tx(gas->ctx, response->freq, response->dst, resp, wait_time);
 	wpabuf_free(resp);
 }
 
@@ -359,12 +405,19 @@
 static void gas_server_handle_tx_status(struct gas_server_response *response,
 					int ack)
 {
-	if (ack && response->offset < wpabuf_len(response->resp)) {
+	if (ack && response->resp &&
+	    response->offset < wpabuf_len(response->resp)) {
 		wpa_printf(MSG_DEBUG,
 			   "GAS: More fragments remaining - keep pending entry");
 		return;
 	}
 
+	if (ack && !response->resp && response->comeback_delay) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Waiting for response - keep pending entry");
+		return;
+	}
+
 	if (!ack)
 		wpa_printf(MSG_DEBUG,
 			   "GAS: No ACK received - drop pending entry");
@@ -415,6 +468,27 @@
 }
 
 
+int gas_server_set_resp(struct gas_server *gas, void *resp_ctx,
+			struct wpabuf *resp)
+{
+	struct gas_server_response *tmp, *response = NULL;
+
+	dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
+			 list) {
+		if (tmp == resp_ctx) {
+			response = tmp;
+			break;
+		}
+	}
+
+	if (!response || response->resp)
+		return -1;
+
+	response->resp = resp;
+	return 0;
+}
+
+
 struct gas_server * gas_server_init(void *ctx,
 				    void (*tx)(void *ctx, int freq,
 					       const u8 *da,
@@ -461,8 +535,9 @@
 int gas_server_register(struct gas_server *gas,
 			const u8 *adv_proto_id, u8 adv_proto_id_len,
 			struct wpabuf *
-			(*req_cb)(void *ctx, const u8 *sa,
-				  const u8 *query, size_t query_len),
+			(*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
+				  const u8 *query, size_t query_len,
+				  u16 *comeback_delay),
 			void (*status_cb)(void *ctx, struct wpabuf *resp,
 					  int ok),
 			void *ctx)
diff --git a/src/common/gas_server.h b/src/common/gas_server.h
index 299f529..2611dde 100644
--- a/src/common/gas_server.h
+++ b/src/common/gas_server.h
@@ -1,6 +1,7 @@
 /*
  * Generic advertisement service (GAS) server
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2020, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,8 +23,9 @@
 int gas_server_register(struct gas_server *gas,
 			const u8 *adv_proto_id, u8 adv_proto_id_len,
 			struct wpabuf *
-			(*req_cb)(void *ctx, const u8 *sa,
-				  const u8 *query, size_t query_len),
+			(*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
+				  const u8 *query, size_t query_len,
+				  u16 *comeback_delay),
 			void (*status_cb)(void *ctx, struct wpabuf *resp,
 					  int ok),
 			void *ctx);
@@ -32,6 +34,8 @@
 		  int freq);
 void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
 			  size_t data_len, int ack);
+int gas_server_set_resp(struct gas_server *gas, void *resp_ctx,
+			struct wpabuf *resp);
 
 #else /* CONFIG_GAS_SERVER */
 
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index f6c67a3..511e68f 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -415,7 +415,7 @@
 			return -1;
 		}
 
-		if (center_idx_to_bw_6ghz(channel) != 0) {
+		if (center_idx_to_bw_6ghz(channel) < 0) {
 			wpa_printf(MSG_ERROR,
 				   "Invalid control channel for 6 GHz band");
 			return -1;
@@ -540,13 +540,20 @@
 		if (center_segment1 ||
 		    (center_segment0 != 0 &&
 		     5000 + center_segment0 * 5 != data->center_freq1 &&
-		     2407 + center_segment0 * 5 != data->center_freq1))
+		     2407 + center_segment0 * 5 != data->center_freq1)) {
+			wpa_printf(MSG_ERROR,
+				   "20/40 MHz: center segment 0 (=%d) and center freq 1 (=%d) not in sync",
+				   center_segment0, data->center_freq1);
 			return -1;
+		}
 		break;
 	case CHANWIDTH_80P80MHZ:
 		if (center_segment1 == center_segment0 + 4 ||
-		    center_segment1 == center_segment0 - 4)
+		    center_segment1 == center_segment0 - 4) {
+			wpa_printf(MSG_ERROR,
+				   "80+80 MHz: center segment 1 only 20 MHz apart");
 			return -1;
+		}
 		data->center_freq2 = 5000 + center_segment1 * 5;
 		/* fall through */
 	case CHANWIDTH_80MHZ:
@@ -555,8 +562,11 @@
 		     center_segment1) ||
 		    (oper_chwidth == CHANWIDTH_80P80MHZ &&
 		     !center_segment1) ||
-		    !sec_channel_offset)
+		    !sec_channel_offset) {
+			wpa_printf(MSG_ERROR,
+				   "80/80+80 MHz: center segment 1 wrong or no second channel offset");
 			return -1;
+		}
 		if (!center_segment0) {
 			if (channel <= 48)
 				center_segment0 = 42;
@@ -582,16 +592,25 @@
 			    center_segment0 == channel - 2 ||
 			    center_segment0 == channel - 6)
 				data->center_freq1 = 5000 + center_segment0 * 5;
-			else
+			else {
+				wpa_printf(MSG_ERROR,
+					   "Wrong coupling between HT and VHT/HE channel setting");
 				return -1;
+			}
 		}
 		break;
 	case CHANWIDTH_160MHZ:
 		data->bandwidth = 160;
-		if (center_segment1)
+		if (center_segment1) {
+			wpa_printf(MSG_ERROR,
+				   "160 MHz: center segment 1 should not be set");
 			return -1;
-		if (!sec_channel_offset)
+		}
+		if (!sec_channel_offset) {
+			wpa_printf(MSG_ERROR,
+				   "160 MHz: second channel offset not set");
 			return -1;
+		}
 		/*
 		 * Note: HT/VHT config and params are coupled. Check if
 		 * HT40 channel band is in VHT160 channel band configuration.
@@ -605,8 +624,11 @@
 		    center_segment0 == channel - 10 ||
 		    center_segment0 == channel - 14)
 			data->center_freq1 = 5000 + center_segment0 * 5;
-		else
+		else {
+			wpa_printf(MSG_ERROR,
+				   "160 MHz: HT40 channel band is not in 160 MHz band");
 			return -1;
+		}
 		break;
 	}
 
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 6859787..72d7dda 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -136,6 +136,10 @@
 		case DPP_CC_OUI_TYPE:
 			/* DPP Configurator Connectivity element */
 			break;
+		case SAE_PK_OUI_TYPE:
+			elems->sae_pk = pos + 4;
+			elems->sae_pk_len = elen - 4;
+			break;
 		default:
 			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
 				   "information element ignored "
@@ -769,6 +773,98 @@
 }
 
 
+/* convert floats with one decimal place to value*10 int, i.e.,
+ * "1.5" will return 15
+ */
+static int hostapd_config_read_int10(const char *value)
+{
+	int i, d;
+	char *pos;
+
+	i = atoi(value);
+	pos = os_strchr(value, '.');
+	d = 0;
+	if (pos) {
+		pos++;
+		if (*pos >= '0' && *pos <= '9')
+			d = *pos - '0';
+	}
+
+	return i * 10 + d;
+}
+
+
+static int valid_cw(int cw)
+{
+	return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
+		cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 ||
+		cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 ||
+		cw == 32767);
+}
+
+
+int hostapd_config_tx_queue(struct hostapd_tx_queue_params tx_queue[],
+			    const char *name, const char *val)
+{
+	int num;
+	const char *pos;
+	struct hostapd_tx_queue_params *queue;
+
+	/* skip 'tx_queue_' prefix */
+	pos = name + 9;
+	if (os_strncmp(pos, "data", 4) == 0 &&
+	    pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
+		num = pos[4] - '0';
+		pos += 6;
+	} else if (os_strncmp(pos, "after_beacon_", 13) == 0 ||
+		   os_strncmp(pos, "beacon_", 7) == 0) {
+		wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
+		return 0;
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos);
+		return -1;
+	}
+
+	if (num >= NUM_TX_QUEUES) {
+		/* for backwards compatibility, do not trigger failure */
+		wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
+		return 0;
+	}
+
+	queue = &tx_queue[num];
+
+	if (os_strcmp(pos, "aifs") == 0) {
+		queue->aifs = atoi(val);
+		if (queue->aifs < 0 || queue->aifs > 255) {
+			wpa_printf(MSG_ERROR, "Invalid AIFS value %d",
+				   queue->aifs);
+			return -1;
+		}
+	} else if (os_strcmp(pos, "cwmin") == 0) {
+		queue->cwmin = atoi(val);
+		if (!valid_cw(queue->cwmin)) {
+			wpa_printf(MSG_ERROR, "Invalid cwMin value %d",
+				   queue->cwmin);
+			return -1;
+		}
+	} else if (os_strcmp(pos, "cwmax") == 0) {
+		queue->cwmax = atoi(val);
+		if (!valid_cw(queue->cwmax)) {
+			wpa_printf(MSG_ERROR, "Invalid cwMax value %d",
+				   queue->cwmax);
+			return -1;
+		}
+	} else if (os_strcmp(pos, "burst") == 0) {
+		queue->burst = hostapd_config_read_int10(val);
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown queue field '%s'", pos);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
 {
 	u8 op_class;
@@ -1683,7 +1779,9 @@
 	S2S(FILS_AUTHENTICATION_FAILURE)
 	S2S(UNKNOWN_AUTHENTICATION_SERVER)
 	S2S(UNKNOWN_PASSWORD_IDENTIFIER)
+	S2S(DENIED_HE_NOT_SUPPORTED)
 	S2S(SAE_HASH_TO_ELEMENT)
+	S2S(SAE_PK)
 	}
 	return "UNKNOWN";
 #undef S2S
@@ -1781,7 +1879,6 @@
 	 */
 	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP },
-	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, P2P_SUPP },
 
 	/*
@@ -1793,6 +1890,12 @@
 	{ HOSTAPD_MODE_IEEE80211AD, 181, 9, 13, 1, BW4320, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211AD, 182, 17, 20, 1, BW6480, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211AD, 183, 25, 27, 1, BW8640, P2P_SUPP },
+
+	/* Keep the operating class 130 as the last entry as a workaround for
+	 * the OneHundredAndThirty Delimiter value used in the Supported
+	 * Operating Classes element to indicate the end of the Operating
+	 * Classes field. */
+	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
 	{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
 };
 
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 55f7aa6..98a55ec 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -114,6 +114,7 @@
 	const u8 *he_operation;
 	const u8 *short_ssid_list;
 	const u8 *he_6ghz_band_cap;
+	const u8 *sae_pk;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -166,6 +167,7 @@
 	u8 he_capabilities_len;
 	u8 he_operation_len;
 	u8 short_ssid_list_len;
+	u8 sae_pk_len;
 
 	struct mb_ies_info mb_ies;
 	struct frag_ies_info frag_ies;
@@ -192,6 +194,18 @@
 
 int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
 			  const char *name, const char *val);
+
+struct hostapd_tx_queue_params {
+	int aifs;
+	int cwmin;
+	int cwmax;
+	int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
+};
+
+#define NUM_TX_QUEUES 4
+
+int hostapd_config_tx_queue(struct hostapd_tx_queue_params queue[],
+			    const char *name, const char *val);
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
 int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
 enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 65cc4df..de41d7f 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -204,7 +204,9 @@
 #define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
 #define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
 #define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123
+#define WLAN_STATUS_DENIED_HE_NOT_SUPPORTED 124
 #define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126
+#define WLAN_STATUS_SAE_PK 127
 
 /* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
 #define WLAN_REASON_UNSPECIFIED 1
@@ -476,6 +478,8 @@
 #define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
 #define WLAN_EID_EXT_EDMG_CAPABILITIES 61
 #define WLAN_EID_EXT_EDMG_OPERATION 62
+#define WLAN_EID_EXT_MSCS_DESCRIPTOR 88
+#define WLAN_EID_EXT_TCLAS_MASK 89
 #define WLAN_EID_EXT_REJECTED_GROUPS 92
 #define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93
 
@@ -560,11 +564,14 @@
 #define WLAN_EXT_CAPAB_SAE_PW_ID 81
 #define WLAN_EXT_CAPAB_SAE_PW_ID_EXCLUSIVELY 82
 #define WLAN_EXT_CAPAB_BEACON_PROTECTION 84
+#define WLAN_EXT_CAPAB_MSCS 85
+#define WLAN_EXT_CAPAB_SAE_PK_EXCLUSIVELY 88
 
 /* Extended RSN Capabilities */
 /* bits 0-3: Field length (n-1) */
 #define WLAN_RSNX_CAPAB_PROTECTED_TWT 4
 #define WLAN_RSNX_CAPAB_SAE_H2E 5
+#define WLAN_RSNX_CAPAB_SAE_PK 6
 
 /* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
 #define WLAN_ACTION_SPECTRUM_MGMT 0
@@ -1325,6 +1332,8 @@
 #define MULTI_AP_OUI_TYPE 0x1B
 #define DPP_CC_IE_VENDOR_TYPE 0x506f9a1e
 #define DPP_CC_OUI_TYPE 0x1e
+#define SAE_PK_IE_VENDOR_TYPE 0x506f9a1f
+#define SAE_PK_OUI_TYPE 0x1f
 
 #define MULTI_AP_SUB_ELEM_TYPE 0x06
 #define MULTI_AP_TEAR_DOWN BIT(4)
@@ -2178,23 +2187,30 @@
 	 /* Minimum MPDU Start Spacing B0..B2
 	  * Maximum A-MPDU Length Exponent B3..B5
 	  * Maximum MPDU Length B6..B7 */
-	u8 a_mpdu_params; /* B0..B7 */
-	u8 info; /* B8..B15 */
+	le16 capab;
 } STRUCT_PACKED;
 
-#define HE_6GHZ_BAND_CAP_MIN_MPDU_START_SPACE_MASK		0x7
-#define HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK	0x7
-#define HE_6GHZ_BAND_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT	3
-#define HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_MASK			0x3
-#define HE_6GHZ_BAND_CAP_MAX_MPDU_LENGTH_SHIFT			6
-
-#define HE_6GHZ_BAND_CAP_SMPS_MASK			  (BIT(1) | BIT(2))
-#define HE_6GHZ_BAND_CAP_SMPS_STATIC			  0
-#define HE_6GHZ_BAND_CAP_SMPS_DYNAMIC			  BIT(1)
-#define HE_6GHZ_BAND_CAP_SMPS_DISABLED			  (BIT(1) | BIT(2))
-#define HE_6GHZ_BAND_CAP_RD_RESPONDER			  BIT(3)
-#define HE_6GHZ_BAND_CAP_RX_ANTENNA_PATTERN		  BIT(4)
-#define HE_6GHZ_BAND_CAP_TX_ANTENNA_PATTERN		  BIT(5)
+#define HE_6GHZ_BAND_CAP_MIN_MPDU_START              (BIT(0) | BIT(1) | BIT(2))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_16K       BIT(3)
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_32K       BIT(4)
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_64K       (BIT(3) | BIT(4))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_128K      BIT(5)
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_256K      (BIT(3) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_512K      (BIT(4) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_1024K     (BIT(3) | BIT(4) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK      (BIT(3) | BIT(4) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT     3
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_7991           BIT(6)
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_11454          BIT(7)
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK           (BIT(6) | BIT(7))
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT	     6
+#define HE_6GHZ_BAND_CAP_SMPS_MASK                   (BIT(9) | BIT(10))
+#define HE_6GHZ_BAND_CAP_SMPS_STATIC                 0
+#define HE_6GHZ_BAND_CAP_SMPS_DYNAMIC                BIT(9)
+#define HE_6GHZ_BAND_CAP_SMPS_DISABLED               (BIT(9) | BIT(10))
+#define HE_6GHZ_BAND_CAP_RD_RESPONDER                BIT(11)
+#define HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS              BIT(12)
+#define HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS              BIT(13)
 
 /*
  * IEEE P802.11ax/D4.0, 9.4.2.246 Spatial Reuse Parameter Set element
@@ -2355,4 +2371,25 @@
 /* DPP Public Action frame identifiers - OUI_WFA */
 #define DPP_OUI_TYPE 0x1A
 
+/* Robust AV streaming Action field values */
+enum robust_av_streaming_action {
+	ROBUST_AV_SCS_REQ = 0,
+	ROBUST_AV_SCS_RESP = 1,
+	ROBUST_AV_GROUP_MEMBERSHIP_REQ = 2,
+	ROBUST_AV_GROUP_MEMBERSHIP_RESP = 3,
+	ROBUST_AV_MSCS_REQ = 4,
+	ROBUST_AV_MSCS_RESP = 5,
+};
+
+enum scs_request_type {
+	SCS_REQ_ADD = 0,
+	SCS_REQ_REMOVE = 1,
+	SCS_REQ_CHANGE = 2,
+};
+
+/* Optional subelement IDs for MSCS Descriptor element */
+enum mscs_description_subelem {
+	MCSC_SUBELEM_STATUS = 1,
+};
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/ocv.c b/src/common/ocv.c
index 06badfb..4bc2749 100644
--- a/src/common/ocv.c
+++ b/src/common/ocv.c
@@ -95,23 +95,24 @@
 }
 
 
-int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
-			 struct wpa_channel_info *ci, int tx_chanwidth,
-			 int tx_seg1_idx)
+enum oci_verify_result
+ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
+		     struct wpa_channel_info *ci, int tx_chanwidth,
+		     int tx_seg1_idx)
 {
 	struct oci_info oci;
 
 	if (!oci_ie) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: did not receive mandatory OCI");
-		return -1;
+			    "did not receive mandatory OCI");
+		return OCI_NOT_FOUND;
 	}
 
 	if (oci_ie_len != 3) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: received OCI of unexpected length (%d)",
+			    "received OCI of unexpected length (%d)",
 			    (int) oci_ie_len);
-		return -1;
+		return OCI_INVALID_LENGTH;
 	}
 
 	os_memset(&oci, 0, sizeof(oci));
@@ -120,25 +121,25 @@
 	oci.seg1_idx = oci_ie[2];
 	if (ocv_derive_all_parameters(&oci) != 0) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: unable to interpret received OCI");
-		return -1;
+			    "unable to interpret received OCI");
+		return OCI_PARSE_ERROR;
 	}
 
 	/* Primary frequency used to send frames to STA must match the STA's */
 	if ((int) ci->frequency != oci.freq) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: primary channel mismatch in received OCI (we use %d but receiver is using %d)",
+			    "primary channel mismatch in received OCI (we use %d but receiver is using %d)",
 			    ci->frequency, oci.freq);
-		return -1;
+		return OCI_PRIMARY_FREQ_MISMATCH;
 	}
 
 	/* We shouldn't transmit with a higher bandwidth than the STA supports
 	 */
 	if (tx_chanwidth > oci.chanwidth) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)",
+			    "channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)",
 			    tx_chanwidth, oci.chanwidth);
-		return -1;
+		return OCI_CHANNEL_WIDTH_MISMATCH;
 	}
 
 	/*
@@ -150,9 +151,9 @@
 	if (tx_chanwidth == 40 && ci->frequency < 2500 &&
 	    ci->sec_channel != oci.sec_channel) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: secondary channel mismatch in received OCI (we use %d but receiver is using %d)",
+			    "secondary channel mismatch in received OCI (we use %d but receiver is using %d)",
 			    ci->sec_channel, oci.sec_channel);
-		return -1;
+		return OCI_SECONDARY_FREQ_MISMATCH;
 	}
 
 	/*
@@ -163,10 +164,10 @@
 	     ci->chanwidth == CHAN_WIDTH_80P80) &&
 	    tx_seg1_idx != oci.seg1_idx) {
 		os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
-			    "OCV failed: frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)",
+			    "frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)",
 			    tx_seg1_idx, oci.seg1_idx);
-		return -1;
+		return OCI_SEG_1_INDEX_MISMATCH;
 	}
 
-	return 0;
+	return OCI_SUCCESS;
 }
diff --git a/src/common/ocv.h b/src/common/ocv.h
index 6379d9d..7fa4522 100644
--- a/src/common/ocv.h
+++ b/src/common/ocv.h
@@ -27,14 +27,21 @@
 #define OCV_OCI_EXTENDED_LEN	(3 + OCV_OCI_LEN)
 #define OCV_OCI_KDE_LEN		(2 + RSN_SELECTOR_LEN + OCV_OCI_LEN)
 
+enum oci_verify_result {
+	OCI_SUCCESS, OCI_NOT_FOUND, OCI_INVALID_LENGTH, OCI_PARSE_ERROR,
+	OCI_PRIMARY_FREQ_MISMATCH, OCI_CHANNEL_WIDTH_MISMATCH,
+	OCI_SECONDARY_FREQ_MISMATCH, OCI_SEG_1_INDEX_MISMATCH
+};
+
 extern char ocv_errorstr[256];
 
 int ocv_derive_all_parameters(struct oci_info *oci);
 int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos);
 int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos);
 int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos);
-int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
-			 struct wpa_channel_info *ci, int tx_chanwidth,
-			 int tx_seg1_idx);
+enum oci_verify_result
+ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
+		     struct wpa_channel_info *ci, int tx_chanwidth,
+		     int tx_seg1_idx);
 
 #endif /* OCV_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index e599b8d..eb6a2c1 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1,7 +1,7 @@
 /*
  * Qualcomm Atheros OUI and vendor specific assignments
  * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
- * Copyright (c) 2018-2019, The Linux Foundation
+ * Copyright (c) 2018-2020, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -174,6 +174,22 @@
  *	to notify the connected station's status. The attributes for this
  *	command are defined in enum qca_wlan_vendor_attr_link_properties.
  *
+ * @QCA_NL80211_VENDOR_SUBCMD_SETBAND: Command to configure the enabled band(s)
+ *	to the driver. This command sets the band(s) through either the
+ *	attribute QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE or
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_MASK (or both).
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE refers enum qca_set_band as unsigned
+ *	integer values and QCA_WLAN_VENDOR_ATTR_SETBAND_MASK refers it as 32
+ *	bit unsigned bitmask values. The allowed values for
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE are limited to QCA_SETBAND_AUTO,
+ *	QCA_SETBAND_5G, and QCA_SETBAND_2G. Other values/bitmasks are valid for
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. The attribute
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE is deprecated and the recommendation
+ *	is to use the QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. If the	both attributes
+ *	are included for backwards compatibility, the configurations through
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_MASK will take the precedence with drivers
+ *	that support both attributes.
+ *
  * @QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY: This command is used to configure
  *	DFS policy and channel hint for ACS operation. This command uses the
  *	attributes defined in enum qca_wlan_vendor_attr_acs_config and
@@ -646,6 +662,44 @@
  *	code immediately prior to triggering cfg80211_disconnected(). The
  *	attributes used with this event are defined in enum
  *	qca_wlan_vendor_attr_driver_disconnect_reason.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC: This vendor subcommand is used to
+ *	add/delete TSPEC for each AC. One command is for one specific AC only.
+ *	This command can only be used in STA mode and the STA must be
+ *	associated with an AP when the command is issued. Uses attributes
+ *	defined in enum qca_wlan_vendor_attr_config_tspec.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT: Vendor subcommand to configure TWT.
+ *	Uses attributes defined in enum qca_wlan_vendor_attr_config_twt.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GETBAND: Command to get the enabled band(s) from
+ *	the driver. The band configurations obtained are referred through
+ *	QCA_WLAN_VENDOR_ATTR_SETBAND_MASK.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS: Vendor subcommand/event for medium
+ *	assessment.
+ *	Uses attributes defined in enum qca_wlan_vendor_attr_medium_assess.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID: This acts as a vendor event and is
+ *	used to update SSID information in hostapd when it is updated in the
+ *	driver. Uses the attribute NL80211_ATTR_SSID.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS: This vendor subcommand is used by
+ *	the driver to send opaque data from the firmware to userspace. The
+ *	driver sends an event to userspace whenever such data is received from
+ *	the firmware.
+ *
+ *	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA is used as the attribute to
+ *	send this opaque data for this event.
+ *
+ *	The format of the opaque data is specific to the particular firmware
+ *	version and there is no guarantee of the format remaining same.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS: This acts as an event.
+ *	The host driver selects Tx VDEV, and notifies user. The attributes
+ *	used with this event are defined in enum
+ *	qca_wlan_vendor_attr_mbssid_tx_vdev_status.
+ *
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -825,6 +879,13 @@
 	QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS_EVENT = 187,
 	QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO = 188,
 	QCA_NL80211_VENDOR_SUBCMD_DRIVER_DISCONNECT_REASON = 189,
+	QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC = 190,
+	QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT = 191,
+	QCA_NL80211_VENDOR_SUBCMD_GETBAND = 192,
+	QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS = 193,
+	QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID = 194,
+	QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS = 195,
+	QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS = 196,
 };
 
 enum qca_wlan_vendor_attr {
@@ -855,7 +916,11 @@
 	QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10,
 	/* Unsigned 32-bit value */
 	QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
-	/* Unsigned 32-bit value from enum qca_set_band. */
+	/* Unsigned 32-bit value from enum qca_set_band. The allowed values for
+	 * this attribute are limited to QCA_SETBAND_AUTO, QCA_SETBAND_5G, and
+	 * QCA_SETBAND_2G. This attribute is deprecated. Recommendation is to
+	 * use QCA_WLAN_VENDOR_ATTR_SETBAND_MASK instead.
+	 */
 	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
 	/* Dummy (NOP) attribute for 64 bit padding */
 	QCA_WLAN_VENDOR_ATTR_PAD = 13,
@@ -1018,6 +1083,15 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_FW_STATE = 42,
 
+	/* Unsigned 32-bitmask value from enum qca_set_band. Substitutes the
+	 * attribute QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE for which only a subset
+	 * of single values from enum qca_set_band are valid. This attribute
+	 * uses bitmask combinations to define the respective allowed band
+	 * combinations and this attributes takes precedence over
+	 * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE if both attributes are included.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SETBAND_MASK = 43,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_MAX	= QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
@@ -1565,9 +1639,10 @@
 };
 
 enum qca_set_band {
-	QCA_SETBAND_AUTO,
-	QCA_SETBAND_5G,
-	QCA_SETBAND_2G,
+	QCA_SETBAND_AUTO = 0,
+	QCA_SETBAND_5G = BIT(0),
+	QCA_SETBAND_2G = BIT(1),
+	QCA_SETBAND_6G = BIT(2),
 };
 
 /**
@@ -1695,6 +1770,8 @@
  *	(not including the Element ID Extension field). Please note that the
  *	draft is still work in progress and this element payload is subject to
  *	change.
+ *
+ *  @QCA_VENDOR_ELEM_ALLPLAY: Allplay element
  */
 enum qca_vendor_element_id {
 	QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
@@ -1703,6 +1780,7 @@
 	QCA_VENDOR_ELEM_RAPS = 3,
 	QCA_VENDOR_ELEM_MU_EDCA_PARAMS = 4,
 	QCA_VENDOR_ELEM_BSS_COLOR_CHANGE = 5,
+	QCA_VENDOR_ELEM_ALLPLAY = 6,
 };
 
 /**
@@ -2120,6 +2198,141 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON = 61,
 
+	/* 32-bit unsigned value to configure different PHY modes to the
+	 * driver/firmware. The possible values are defined in
+	 * enum qca_wlan_vendor_phy_mode. The configuration will be reset to
+	 * default value, i.e., QCA_WLAN_VENDOR_PHY_MODE_AUTO upon restarting
+	 * the driver.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE = 62,
+
+	/* 8-bit unsigned value to configure the maximum supported channel width
+	 * for STA mode. If this value is configured when STA is in connected
+	 * state, it should not exceed the negotiated channel width. If it is
+	 * configured when STA is in disconnected state, the configured value
+	 * will take effect for the next immediate connection.
+	 * Possible values are:
+	 *   NL80211_CHAN_WIDTH_20
+	 *   NL80211_CHAN_WIDTH_40
+	 *   NL80211_CHAN_WIDTH_80
+	 *   NL80211_CHAN_WIDTH_80P80
+	 *   NL80211_CHAN_WIDTH_160
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH = 63,
+
+	/* 8-bit unsigned value to enable/disable dynamic bandwidth adjustment.
+	 * This attribute is only applicable for STA mode. When dynamic
+	 * bandwidth adjustment is disabled, STA will use static channel width
+	 * the value of which is negotiated during connection.
+	 * 1-enable (default), 0-disable
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_BW = 64,
+
+	/* 8-bit unsigned value to configure the maximum number of subframes of
+	 * TX MSDU for aggregation. Possible values are 0-31. When set to 0,
+	 * it is decided by the hardware.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MSDU_AGGREGATION = 65,
+
+	/* 8-bit unsigned value to configure the maximum number of subframes of
+	 * RX MSDU for aggregation. Possible values are 0-31. When set to 0,
+	 * it is decided by the hardware.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MSDU_AGGREGATION = 66,
+
+	/* 8-bit unsigned value. This attribute is used to dynamically
+	 * enable/disable the LDPC capability of the device. When configured in
+	 * the disconnected state, the updated configuration will be considered
+	 * for the immediately following connection attempt. If this
+	 * configuration is modified while the device is in the connected state,
+	 * the LDPC TX will be updated with this configuration immediately,
+	 * while the LDPC RX configuration update will take place starting from
+	 * the subsequent association attempt.
+	 * 1-Enable, 0-Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_LDPC = 67,
+
+	/* 8-bit unsigned value. This attribute is used to dynamically
+	 * enable/disable the TX STBC capability of the device. When configured
+	 * in the disconnected state, the updated configuration will be
+	 * considered for the immediately following connection attempt. If the
+	 * connection is formed with TX STBC enabled and if this configuration
+	 * is disabled during that association, the TX will be impacted
+	 * immediately. Further connection attempts will disable TX STBC.
+	 * However, enabling the TX STBC for a connected session with disabled
+	 * capability is not allowed and will fail.
+	 * 1-Enable, 0-Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TX_STBC = 68,
+
+	/* 8-bit unsigned value. This attribute is used to dynamically
+	 * enable/disable the RX STBC capability of the device. When configured
+	 * in the disconnected state, the updated configuration will be
+	 * considered for the immediately following connection attempt. If the
+	 * configuration is modified in the connected state, there will be no
+	 * impact for the current association, but further connection attempts
+	 * will use the updated configuration.
+	 * 1-Enable, 0-Disable.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC = 69,
+
+	/* 8-bit unsigned value. This attribute is used to dynamically configure
+	 * the number of spatial streams. When configured in the disconnected
+	 * state, the updated configuration will be considered for the
+	 * immediately following connection attempt. If the NSS is updated after
+	 * the connection, the updated NSS value is notified to the peer using
+	 * the Operating Mode Notification/Spatial Multiplexing Power Save
+	 * frame. The updated NSS value after the connection shall not be
+	 * greater than the one negotiated during the connection. Any such
+	 * higher value configuration shall be returned with a failure.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_NSS = 70,
+	/* 8-bit unsigned value to trigger Optimized Power Management:
+	 * 1-Enable, 0-Disable
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT = 71,
+
+	/* 8-bit unsigned value. This attribute takes the QoS/access category
+	 * value represented by the enum qca_wlan_ac_type and expects the driver
+	 * to upgrade the UDP frames to this access category. The value of
+	 * QCA_WLAN_AC_ALL is invalid for this attribute. This will override the
+	 * DSCP value configured in the frame with the intention to only upgrade
+	 * the access category. That said, it is not intended to downgrade the
+	 * access category for the frames.
+	 * Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be
+	 * disabled, as BK is of the lowest priority and an upgrade to it does
+	 * not result in any changes for the frames.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE = 72,
+
+	/* 8-bit unsigned value. This attribute is used to dynamically configure
+	 * the number of chains to be used for transmitting data. This
+	 * configuration is allowed only when in connected state and will be
+	 * effective until disconnected. The driver rejects this configuration
+	 * if the number of spatial streams being used in the current connection
+	 * cannot be supported by this configuration.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS = 73,
+	/* 8-bit unsigned value. This attribute is used to dynamically configure
+	 * the number of chains to be used for receiving data. This
+	 * configuration is allowed only when in connected state and will be
+	 * effective until disconnected. The driver rejects this configuration
+	 * if the number of spatial streams being used in the current connection
+	 * cannot be supported by this configuration.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS = 74,
+
+	/* 8-bit unsigned value to configure ANI setting type.
+	 * See &enum qca_wlan_ani_setting for possible values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_SETTING = 75,
+	/* 32-bit signed value to configure ANI level. This is used when
+	 * ANI settings type is &QCA_WLAN_ANI_SETTING_FIXED.
+	 * The set and get of ANI level with &QCA_WLAN_ANI_SETTING_AUTO
+	 * is invalid, the driver will return a failure.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_LEVEL = 76,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -2135,6 +2348,16 @@
 	QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL
 
 /**
+ * enum qca_wlan_ani_setting - ANI setting type
+ * @QCA_WLAN_ANI_SETTING_AUTO: Automatically determine ANI level
+ * @QCA_WLAN_ANI_SETTING_FIXED: Fix ANI level to the dBm parameter
+ */
+enum qca_wlan_ani_setting {
+	QCA_WLAN_ANI_SETTING_AUTO = 0,
+	QCA_WLAN_ANI_SETTING_FIXED = 1,
+};
+
+/**
  * enum qca_wlan_vendor_attr_sap_config - Parameters for AP configuration
  *
  * @QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL: Optional (u8)
@@ -2189,19 +2412,54 @@
 
 /**
  * enum qca_wlan_gpio_attr - Parameters for GPIO configuration
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND: Required (u32)
+ * value to specify the GPIO command. Please refer to enum qca_gpio_cmd_type
+ * for the available values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM: Required (u32)
+ * value to specify the GPIO number.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG or %QCA_WLAN_VENDOR_GPIO_OUTPUT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE: Required (u32)
+ * value to specify the GPIO output level. Please refer to enum qca_gpio_value
+ * for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_OUTPUT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE: Required (u32)
+ * value to specify the GPIO pull type. Please refer to enum qca_gpio_pull_type
+ * for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE: Required (u32)
+ * value to specify the GPIO interrupt mode. Please refer to enum
+ * qca_gpio_interrupt_mode for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR: Required (u32)
+ * value to specify the GPIO direction. Please refer to enum qca_gpio_direction
+ * for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG.
  */
 enum qca_wlan_gpio_attr {
 	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INVALID = 0,
 	/* Unsigned 32-bit attribute for GPIO command */
-	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND = 1,
 	/* Unsigned 32-bit attribute for GPIO PIN number to configure */
-	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM = 2,
 	/* Unsigned 32-bit attribute for GPIO value to configure */
-	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE = 3,
 	/* Unsigned 32-bit attribute for GPIO pull type */
-	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE = 4,
 	/* Unsigned 32-bit attribute for GPIO interrupt mode */
-	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE = 5,
+	/* Unsigned 32-bit attribute for GPIO direction to configure */
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR = 6,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST,
@@ -2210,6 +2468,73 @@
 };
 
 /**
+ * enum gpio_cmd_type - GPIO configuration command type
+ * @QCA_WLAN_VENDOR_GPIO_CONFIG: Set GPIO configuration info
+ * @QCA_WLAN_VENDOR_GPIO_OUTPUT: Set GPIO output level
+ */
+enum qca_gpio_cmd_type {
+	QCA_WLAN_VENDOR_GPIO_CONFIG = 0,
+	QCA_WLAN_VENDOR_GPIO_OUTPUT = 1,
+};
+
+/**
+ * enum qca_gpio_pull_type - GPIO pull type
+ * @QCA_WLAN_GPIO_PULL_NONE: Set GPIO pull type to none
+ * @QCA_WLAN_GPIO_PULL_UP: Set GPIO pull up
+ * @QCA_WLAN_GPIO_PULL_DOWN: Set GPIO pull down
+ */
+enum qca_gpio_pull_type {
+	QCA_WLAN_GPIO_PULL_NONE = 0,
+	QCA_WLAN_GPIO_PULL_UP = 1,
+	QCA_WLAN_GPIO_PULL_DOWN = 2,
+	QCA_WLAN_GPIO_PULL_MAX,
+};
+
+/**
+ * enum qca_gpio_direction - GPIO direction
+ * @QCA_WLAN_GPIO_INPUT: Set GPIO as input mode
+ * @QCA_WLAN_GPIO_OUTPUT: Set GPIO as output mode
+ * @QCA_WLAN_GPIO_VALUE_MAX: Invalid value
+ */
+enum qca_gpio_direction {
+	QCA_WLAN_GPIO_INPUT = 0,
+	QCA_WLAN_GPIO_OUTPUT = 1,
+	QCA_WLAN_GPIO_DIR_MAX,
+};
+
+/**
+ * enum qca_gpio_value - GPIO Value
+ * @QCA_WLAN_GPIO_LEVEL_LOW: set gpio output level to low
+ * @QCA_WLAN_GPIO_LEVEL_HIGH: set gpio output level to high
+ * @QCA_WLAN_GPIO_LEVEL_MAX: Invalid value
+ */
+enum qca_gpio_value {
+	QCA_WLAN_GPIO_LEVEL_LOW = 0,
+	QCA_WLAN_GPIO_LEVEL_HIGH = 1,
+	QCA_WLAN_GPIO_LEVEL_MAX,
+};
+
+/**
+ * enum gpio_interrupt_mode - GPIO interrupt mode
+ * @QCA_WLAN_GPIO_INTMODE_DISABLE: Disable interrupt trigger
+ * @QCA_WLAN_GPIO_INTMODE_RISING_EDGE: Interrupt with GPIO rising edge trigger
+ * @QCA_WLAN_GPIO_INTMODE_FALLING_EDGE: Interrupt with GPIO falling edge trigger
+ * @QCA_WLAN_GPIO_INTMODE_BOTH_EDGE: Interrupt with GPIO both edge trigger
+ * @QCA_WLAN_GPIO_INTMODE_LEVEL_LOW: Interrupt with GPIO level low trigger
+ * @QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH: Interrupt with GPIO level high trigger
+ * @QCA_WLAN_GPIO_INTMODE_MAX: Invalid value
+ */
+enum qca_gpio_interrupt_mode {
+	QCA_WLAN_GPIO_INTMODE_DISABLE = 0,
+	QCA_WLAN_GPIO_INTMODE_RISING_EDGE = 1,
+	QCA_WLAN_GPIO_INTMODE_FALLING_EDGE = 2,
+	QCA_WLAN_GPIO_INTMODE_BOTH_EDGE = 3,
+	QCA_WLAN_GPIO_INTMODE_LEVEL_LOW = 4,
+	QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH = 5,
+	QCA_WLAN_GPIO_INTMODE_MAX,
+};
+
+/**
  * qca_wlan_set_qdepth_thresh_attr - Parameters for setting
  * MSDUQ depth threshold per peer per tid in the target
  *
@@ -3297,7 +3622,7 @@
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66,
 
 	/* Signifies the nested list of channel attributes
-	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_*
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_*
 	 */
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67,
 
@@ -3359,6 +3684,17 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU = 83,
 
+	/* u32 value representing total time in milliseconds for which the radio
+	 * is transmitting on this channel. This attribute will be nested
+	 * within QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_TX_TIME = 84,
+	/* u32 value representing total time in milliseconds for which the radio
+	 * is receiving all 802.11 frames intended for this device on this
+	 * channel. This attribute will be nested within
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_RX_TIME = 85,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
@@ -3706,6 +4042,30 @@
 	QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_roam_scan_scheme: Scan scheme
+ *
+ * @QCA_ROAM_SCAN_SCHEME_NO_SCAN: No frequencies specified to scan.
+ *     Indicates the driver to not scan on a Roam Trigger scenario, but
+ *     disconnect. E.g., on a BTM request from the AP the driver/firmware shall
+ *     disconnect from the current connected AP by notifying a failure
+ *     code in the BTM response.
+ *
+ * @QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN: Indicates the driver/firmware to
+ *     trigger partial frequency scans. These frequencies are the ones learned
+ *     or maintained by the driver based on the probability of finding the
+ *     BSSIDs in the ESS for which the roaming is triggered.
+ *
+ * @QCA_ROAM_SCAN_SCHEME_FULL_SCAN: Indicates the driver/firmware to
+ *     trigger the scan on all the valid frequencies to find better
+ *     candidates to roam.
+ */
+enum qca_roam_scan_scheme {
+	QCA_ROAM_SCAN_SCHEME_NO_SCAN = 0,
+	QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN = 1,
+	QCA_ROAM_SCAN_SCHEME_FULL_SCAN = 2,
+};
+
 /*
  * enum qca_vendor_roam_triggers: Bitmap of roaming triggers
  *
@@ -3726,6 +4086,18 @@
  *	when BTM Request frame is received from the connected AP.
  * @QCA_ROAM_TRIGGER_REASON_BSS_LOAD: Set if the roam has to be triggered
  *	when the channel utilization is goes above the configured threshold.
+ * @QCA_ROAM_TRIGGER_REASON_USER_TRIGGER: Set if the roam has to be triggered
+ *	based on the request from the user (space).
+ * @QCA_ROAM_TRIGGER_REASON_DEAUTH: Set if the roam has to be triggered when
+ *	device receives Deauthentication/Disassociation frame from connected AP.
+ * @QCA_ROAM_TRIGGER_REASON_IDLE: Set if the roam has to be triggered when the
+ *	device is in idle state (no TX/RX) and suspend mode, if the current RSSI
+ *	is determined to be a poor one.
+ * @QCA_ROAM_TRIGGER_REASON_TX_FAILURES: Set if the roam has to be triggered
+ *	based on continuous TX Data frame failures to the connected AP.
+ * @QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN: Set if the roam has to be triggered
+ *	based on the scan results obtained from an external scan (not triggered
+ *	to aim roaming).
  *
  * Set the corresponding roam trigger reason bit to consider it for roam
  * trigger.
@@ -3741,6 +4113,11 @@
 	QCA_ROAM_TRIGGER_REASON_DENSE		= 1 << 5,
 	QCA_ROAM_TRIGGER_REASON_BTM		= 1 << 6,
 	QCA_ROAM_TRIGGER_REASON_BSS_LOAD	= 1 << 7,
+	QCA_ROAM_TRIGGER_REASON_USER_TRIGGER	= 1 << 8,
+	QCA_ROAM_TRIGGER_REASON_DEAUTH          = 1 << 9,
+	QCA_ROAM_TRIGGER_REASON_IDLE		= 1 << 10,
+	QCA_ROAM_TRIGGER_REASON_TX_FAILURES	= 1 << 11,
+	QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN	= 1 << 12,
 };
 
 /**
@@ -3897,6 +4274,40 @@
  *
  *	Clears the selection criteria configured in the driver when specified
  *	with clear command.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME: Unsigned 32-bit value.
+ *	Represents value of the scan frequency scheme from enum
+ *	qca_roam_scan_scheme.
+ *	It's an optional attribute. If this attribute is not configured, the
+ *	driver shall proceed with default behavior.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD: Signed 32-bit value in dBm,
+ *	signifying the RSSI threshold of the current connected AP, indicating
+ *	the driver to trigger roam only when the current connected AP's RSSI
+ *	is less than this threshold.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD: Signed 32-bit value in dBm,
+ *	signifying the RSSI threshold of the candidate AP, indicating
+ *	the driver to trigger roam only to the candidate AP with RSSI
+ *	better than this threshold.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_USER_REASON: Unsigned 32-bit value. Represents the
+ *	user defined reason code to be sent to the AP in response to AP's
+ *	request to trigger the roam if the roaming cannot be triggered.
+ *	Applies to all the scenarios of AP assisted roaming (e.g., BTM).
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS: Unsigned 32-bit value.
+ *	Carries a bitmap of the roam triggers specified in
+ *	enum qca_vendor_roam_triggers.
+ *	Represents the roam triggers for which the specific scan scheme from
+ *	enum qca_roam_scan_scheme has to be applied.
+ *	It's an optional attribute. If this attribute is not configured, but
+ *	QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME is specified, the scan scheme
+ *	specified through QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME is applicable for
+ *	all the roams.
+ *	If both QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME and
+ *	QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS are not specified, the
+ *	driver shall proceed with the default behavior.
  */
 enum qca_vendor_attr_roam_control {
 	QCA_ATTR_ROAM_CONTROL_ENABLE = 1,
@@ -3907,6 +4318,11 @@
 	QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD = 6,
 	QCA_ATTR_ROAM_CONTROL_TRIGGERS = 7,
 	QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA = 8,
+	QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME = 9,
+	QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD = 10,
+	QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD = 11,
+	QCA_ATTR_ROAM_CONTROL_USER_REASON = 12,
+	QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS = 13,
 
 	/* keep last */
 	QCA_ATTR_ROAM_CONTROL_AFTER_LAST,
@@ -3926,7 +4342,7 @@
  *	Represents the Request ID for the specific set of commands.
  *	This also helps to map specific set of commands to the respective
  *	ID / client. e.g., helps to identify the user entity configuring the
- *	Blacklist BSSID and accordingly clear the respective ones with the
+ *	ignored BSSIDs and accordingly clear the respective ones with the
  *	matching ID.
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS: Unsigned
@@ -3993,17 +4409,18 @@
  *	the BSSID for the purpose of comparing it with other roam candidate.
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS: Nested attribute,
- *	represents the BSSIDs to get blacklisted for roaming.
+ *	represents the BSSIDs to get ignored for roaming.
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID: Unsigned
- *	32-bit value, represents the number of blacklisted BSSIDs.
+ *	32-bit value, represents the number of ignored BSSIDs.
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID: 6-byte MAC
- *	address representing the Blacklisted BSSID.
+ *	address representing the ignored BSSID.
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT: Flag attribute,
- *	indicates this BSSID blacklist as a hint to the driver. The driver can
- *	select this BSSID in the worst case (when no other BSSIDs are better).
+ *	indicates this request to ignore the BSSID as a hint to the driver. The
+ *	driver can select this BSSID in the worst case (when no other BSSIDs are
+ *	better).
  *
  * @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL: Nested attribute to
  *	set/get/clear the roam control config as
@@ -4038,11 +4455,11 @@
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID = 16,
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER = 17,
 
-	/* Attribute for set_blacklist bssid params */
+	/* Attribute for setting ignored BSSID parameters */
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18,
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19,
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20,
-	/* Flag attribute indicates this BSSID blacklist as a hint */
+	/* Flag attribute indicates this entry as a hint */
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21,
 
 	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL = 22,
@@ -4078,9 +4495,9 @@
  *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS to set the BSSID
  *	preference.
  *
- * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: Sets the Blacklist
- *	BSSIDs. Refers QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS to
- *	set the same.
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: Sets the list of BSSIDs
+ *	to ignore in roaming decision. Uses
+ *	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS to set the list.
  *
  * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET: Command to set the
  *	roam control config to the driver with the attribute
@@ -4742,9 +5159,9 @@
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_ADHOC  = 1 << 6,
 	/* Station only channel */
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_HOSTAP = 1 << 7,
-	/* DFS radar history for slave device (STA mode) */
+	/* DFS radar history for client device (STA mode) */
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_HISTORY_RADAR   = 1 << 8,
-	/* DFS CAC valid for slave device (STA mode) */
+	/* DFS CAC valid for client device (STA mode) */
 	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CAC_VALID       = 1 << 9,
 };
 
@@ -6471,8 +6888,9 @@
 
 /**
  * enum qca_wlan_vendor_thermal_level - Defines various thermal levels
- * configured by userspace to the driver/firmware. The values will be
- * encapsulated in QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL attribute.
+ * configured by userspace to the driver/firmware.
+ * The values can be encapsulated in QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL or
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL attribute.
  * The driver/firmware takes actions requested by userspace such as throttling
  * wifi TX etc. in order to mitigate high temperature.
  *
@@ -6506,8 +6924,9 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE = 1,
 	/* Userspace uses this attribute to configure thermal level to the
-	 * driver/firmware. Used in request, u32 attribute, possible values
-	 * are defined in enum qca_wlan_vendor_thermal_level.
+	 * driver/firmware, or get thermal level from the driver/firmware.
+	 * Used in request or response, u32 attribute,
+	 * possible values are defined in enum qca_wlan_vendor_thermal_level.
 	 */
 	QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL = 2,
 	/* Userspace uses this attribute to configure the time in which the
@@ -6545,6 +6964,9 @@
  * resume action.
  * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL: Configure thermal level to
  * the driver/firmware.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL: Request to get the current
+ * thermal level from the driver/firmware. The driver should respond with a
+ * thermal level defined in enum qca_wlan_vendor_thermal_level.
  */
 enum qca_wlan_vendor_attr_thermal_cmd_type {
 	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS,
@@ -6552,6 +6974,7 @@
 	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND,
 	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME,
 	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL,
+	QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL,
 };
 
 /**
@@ -6634,6 +7057,11 @@
 	 * NLA_FLAG attribute.
 	 */
 	QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_RESUME_COMPLETE,
+	/* Thermal level from the driver.
+	 * u32 attribute. Possible values are defined in
+	 * enum qca_wlan_vendor_thermal_level.
+	 */
+	QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL = 3,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST,
@@ -6847,6 +7275,72 @@
 	QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST - 1,
 };
 
+ /**
+  * enum qca_wlan_vendor_phy_mode - Different PHY modes
+  * These values are used with %QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE.
+  *
+  * @QCA_WLAN_VENDOR_PHY_MODE_AUTO: autoselect
+  * @QCA_WLAN_VENDOR_PHY_MODE_2G_AUTO: 2.4 GHz 802.11b/g/n/ax autoselect
+  * @QCA_WLAN_VENDOR_PHY_MODE_5G_AUTO: 5 GHz 802.11a/n/ac/ax autoselect
+  * @QCA_WLAN_VENDOR_PHY_MODE_11A: 5 GHz, OFDM
+  * @QCA_WLAN_VENDOR_PHY_MODE_11B: 2.4 GHz, CCK
+  * @QCA_WLAN_VENDOR_PHY_MODE_11G: 2.4 GHz, OFDM
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AGN: Support 802.11n in both 2.4 GHz and 5 GHz
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT20: 2.4 GHz, HT20
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40PLUS: 2.4 GHz, HT40 (ext ch +1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40MINUS: 2.4 GHz, HT40 (ext ch -1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40: 2.4 GHz, Auto HT40
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT20: 5 GHz, HT20
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40PLUS: 5 GHz, HT40 (ext ch +1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40MINUS: 5 GHz, HT40 (ext ch -1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40: 5 GHz, Auto HT40
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT20: 5 GHz, VHT20
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40PLUS: 5 GHz, VHT40 (Ext ch +1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40MINUS: 5 GHz VHT40 (Ext ch -1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40: 5 GHz, VHT40
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80: 5 GHz, VHT80
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80P80: 5 GHz, VHT80+80
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT160: 5 GHz, VHT160
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE20: HE20
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40: HE40
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40PLUS: HE40 (ext ch +1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40MINUS: HE40 (ext ch -1)
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80: HE80
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80: HE 80P80
+  * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160: HE160
+  */
+enum qca_wlan_vendor_phy_mode {
+	QCA_WLAN_VENDOR_PHY_MODE_AUTO = 0,
+	QCA_WLAN_VENDOR_PHY_MODE_2G_AUTO = 1,
+	QCA_WLAN_VENDOR_PHY_MODE_5G_AUTO = 2,
+	QCA_WLAN_VENDOR_PHY_MODE_11A = 3,
+	QCA_WLAN_VENDOR_PHY_MODE_11B = 4,
+	QCA_WLAN_VENDOR_PHY_MODE_11G = 5,
+	QCA_WLAN_VENDOR_PHY_MODE_11AGN = 6,
+	QCA_WLAN_VENDOR_PHY_MODE_11NG_HT20 = 7,
+	QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40PLUS = 8,
+	QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40MINUS = 9,
+	QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40 = 10,
+	QCA_WLAN_VENDOR_PHY_MODE_11NA_HT20 = 11,
+	QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40PLUS = 12,
+	QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40MINUS = 13,
+	QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40 = 14,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT20 = 15,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40PLUS = 16,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40MINUS = 17,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40 = 18,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80 = 19,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80P80 = 20,
+	QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT160 = 21,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE20 = 22,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40 = 23,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40PLUS = 24,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40MINUS = 25,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80 = 26,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80 = 27,
+	QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160 = 28,
+};
+
 /* Attributes for data used by
  * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION
  */
@@ -7130,6 +7624,67 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37,
 
+	/* 8-bit unsigned value to configure protection for Management
+	 * frames when PMF is enabled for the association.
+	 * This attribute is used to configure the testbed device.
+	 * 0-use the correct key, 1-use an incorrect key, 2-disable protection.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PMF_PROTECTION = 38,
+
+	/* Flag attribute to inject Disassociation frame to the connected AP.
+	 * This attribute is used to configure the testbed device.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISASSOC_TX = 39,
+
+	/* 8-bit unsigned value to configure an override for the RSNXE Used
+	 * subfield in the MIC control field of the FTE in FT Reassociation
+	 * Request frame.
+	 * 0 - Default behavior, 1 - override with 1, 2 - override with 0.
+	 * This attribute is used to configure the testbed device.
+	 * This attribute can be configured only when STA is in associated state
+	 * and the configuration is valid until the disconnection.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FT_REASSOCREQ_RSNXE_USED = 40,
+
+	/* 8-bit unsigned value to configure the driver to ignore CSA (Channel
+	 * Switch Announcement) when STA is in connected state.
+	 * 0 - Default behavior, 1 - Ignore CSA.
+	 * This attribute is used to configure the testbed device.
+	 * This attribute can be configured only when STA is in associated state
+	 * and the configuration is valid until the disconnection.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_CSA = 41,
+
+	/* Nested attribute values required to configure OCI (Operating Channel
+	 * Information). Attributes defined in enum
+	 * qca_wlan_vendor_attr_oci_override are nested within this attribute.
+	 * This attribute is used to configure the testbed device.
+	 * This attribute can be configured only when STA is in associated state
+	 * and the configuration is valid until the disconnection.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE = 42,
+
+	/* 8-bit unsigned value to configure the driver/firmware to ignore SA
+	 * Query timeout. If this configuration is enabled STA shall not send
+	 * Deauthentication frmae when SA Query times out (mainly, after a
+	 * channel switch when OCV is enabled).
+	 * 0 - Default behavior, 1 - Ignore SA Query timeout.
+	 * This attribute is used to configure the testbed device.
+	 * This attribute can be configured only when STA is in associated state
+	 * and the configuration is valid until the disconnection.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_SA_QUERY_TIMEOUT = 43,
+
+	/* 8-bit unsigned value to configure the driver/firmware to start or
+	 * stop transmitting FILS discovery frames.
+	 * 0 - Stop transmitting FILS discovery frames
+	 * 1 - Start transmitting FILS discovery frames
+	 * This attribute is used to configure the testbed device.
+	 * This attribute can be configured only in AP mode and the
+	 * configuration is valid until AP restart.
+	 */
+	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FILS_DISCOVERY_FRAMES_TX = 44,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -7137,6 +7692,63 @@
 };
 
 /**
+ * enum qca_wlan_twt_operation - Operation of the config TWT request
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION.
+ *
+ * @QCA_WLAN_TWT_SET: Setup a TWT session. Required parameters are configured
+ * through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup.
+ *
+ * @QCA_WLAN_TWT_GET: Get the configured TWT parameters. Required parameters are
+ * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup.
+ *
+ * @QCA_WLAN_TWT_TERMINATE: Terminate the TWT session. Required parameters are
+ * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup.
+ *
+ * @QCA_WLAN_TWT_SUSPEND: Suspend the TWT session. Required parameters are
+ * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup.
+ *
+ * @QCA_WLAN_TWT_RESUME: Resume the TWT session. Required parameters are
+ * configured through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_resume. Valid only after the TWT session is setup.
+ */
+enum qca_wlan_twt_operation {
+	QCA_WLAN_TWT_SET = 0,
+	QCA_WLAN_TWT_GET = 1,
+	QCA_WLAN_TWT_TERMINATE = 2,
+	QCA_WLAN_TWT_SUSPEND = 3,
+	QCA_WLAN_TWT_RESUME = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_config_twt: Defines attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION: u8 attribute. Specify the TWT
+ * operation of this request. Possible values are defined in enum
+ * qca_wlan_twt_operation. The parameters for the respective operation is
+ * specified through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS: Nested attribute representing the
+ * parameters configured for TWT. These parameters are represented by
+ * enum qca_wlan_vendor_attr_twt_setup or enum qca_wlan_vendor_attr_twt_resume
+ * based on the operation.
+ */
+enum qca_wlan_vendor_attr_config_twt {
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION = 1,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_MAX =
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST - 1,
+};
+
+/**
  * enum qca_wlan_vendor_attr_bss_filter - Used by the vendor command
  * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
  * The user can add/delete the filter by specifying the BSSID/STA MAC address in
@@ -7287,7 +7899,8 @@
  * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for
  * TWT (Target Wake Time) setup request. These attributes are sent as part of
  * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and
- * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by
+ * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute.
  * Disable (flag attribute not present) - Individual TWT
@@ -7297,10 +7910,13 @@
  * STA and AP.
  * Broadcast means the session is across multiple STAs and an AP. The
  * configuration parameters are announced in Beacon frames by the AP.
+ * This is used in
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE: Required (u8).
  * Unsigned 8-bit qca_wlan_vendor_twt_setup_req_type to
- * specify the TWT request type
+ * specify the TWT request type. This is used in TWT SET operation.
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER: Flag attribute
  * Enable (flag attribute present) - TWT with trigger support.
@@ -7308,40 +7924,113 @@
  * Trigger means the AP will send the trigger frame to allow STA to send data.
  * Without trigger, the STA will wait for the MU EDCA timer before
  * transmitting the data.
+ * This is used in
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE: Required (u8)
  * 0 - Announced TWT - In this mode, STA may skip few service periods to
  * save more power. If STA wants to wake up, it will send a PS-POLL/QoS
  * NULL frame to AP.
  * 1 - Unannounced TWT - The STA will wakeup during every SP.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID: Optional (u8)
  * Flow ID is the unique identifier for each TWT session.
- * Currently this is not required and dialog ID will be set to zero.
+ * If not provided then dialog ID will be set to zero.
+ * This is an optional parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Request and Response
+ * 3. TWT TERMINATE Request and Response
+ * 4. TWT SUSPEND Request and Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8)
  * This attribute (exp) is used along with the mantissa to derive the
  * wake interval using the following formula:
  * pow(2,exp) = wake_intvl_us/wake_intvl_mantis
  * Wake interval is the interval between 2 successive SP.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION: Flag attribute
  * Enable (flag attribute present) - Protection required.
  * Disable (flag attribute not present) - Protection not required.
  * If protection is enabled, then the AP will use protection
  * mechanism using RTS/CTS to self to reserve the airtime.
+ * This is used in
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME: Optional (u32)
  * This attribute is used as the SP offset which is the offset from
  * TSF after which the wake happens. The units are in microseconds. If
  * this attribute is not provided, then the value will be set to zero.
+ * This is an optional parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION: Required (u32)
- * This is the duration of the service period. The units are in TU.
+ * This is the duration of the service period. This is specified as
+ * multiples of 256 microseconds. Valid values are 0x1 to 0xFF.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA: Required (u32)
  * This attribute is used to configure wake interval mantissa.
  * The units are in TU.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS: Required (u8)
+ * This field is applicable for TWT response only.
+ * This contains status values in enum qca_wlan_vendor_twt_status
+ * and is passed to the userspace. This is used in TWT SET operation.
+ * This is a required parameter for
+ * 1. TWT SET Response
+ * 2. TWT TERMINATE Response
+ * 3. TWT SUSPEND Response
+ * 4. TWT RESUME Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESP_TYPE: Required (u8)
+ * This field is applicable for TWT response only.
+ * This field contains response type from the TWT responder and is
+ * passed to the userspace. The values for this field are defined in
+ * enum qca_wlan_vendor_twt_setup_resp_type. This is used in TWT SET
+ * response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME_TSF: Required (u64)
+ * This field is applicable for TWT response only.
+ * This field contains absolute TSF value of the wake time received
+ * from the TWT responder and is passed to the userspace.
+ * This is a required parameter for
+ * 1. TWT SET Response
+ * 2. TWT GET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED: Flag attribute.
+ * Enable (flag attribute present) - Indicates that the TWT responder
+ * supports reception of TWT information frame from the TWT requestor.
+ * Disable (flag attribute not present) - Indicates that the responder
+ * doesn't support reception of TWT information frame from requestor.
+ * This is used in
+ * 1. TWT SET Response
+ * 2. TWT GET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR: 6-byte MAC address
+ * Represents the MAC address of the peer for which the TWT session
+ * is being configured. This is used in AP mode to represent the respective
+ * client. In AP mode, this is an optional parameter for response and is
+ * a required parameter for
+ * 1. TWT SET Request
+ * 2. TWT GET Request
+ * 3. TWT TERMINATE Request
+ * 4. TWT SUSPEND Request
+ * In STA mode, this is an optional parameter in request and response for
+ * the above four TWT operations.
  */
 enum qca_wlan_vendor_attr_twt_setup {
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
@@ -7356,6 +8045,14 @@
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION = 9,
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA = 10,
 
+	/* TWT Response only attributes */
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS = 11,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESP_TYPE = 12,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME_TSF = 13,
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED = 14,
+
+	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR = 15,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX =
@@ -7363,24 +8060,83 @@
 };
 
 /**
- * enum qca_wlan_vendor_attr_twt_resume: Represents attributes for
+ * enum qca_wlan_vendor_twt_status - Represents the status of the requested
+ * TWT operation
+ *
+ * @QCA_WLAN_VENDOR_TWT_STATUS_OK: TWT request successfully completed
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_ENABLED: TWT not enabled
+ * @QCA_WLAN_VENDOR_TWT_STATUS_USED_DIALOG_ID: TWT dialog ID is already used
+ * @QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY: TWT session is busy
+ * @QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST: TWT session does not exist
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NOT_SUSPENDED: TWT session not in suspend state
+ * @QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM: Invalid parameters
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NOT_READY: FW not ready
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE: FW resource exhausted
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK: Peer AP/STA did not ACK the
+ * request/response frame
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NO_RESPONSE: Peer AP did not send the response
+ * frame
+ * @QCA_WLAN_VENDOR_TWT_STATUS_DENIED: AP did not accept the request
+ * @QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR: Adding TWT dialog failed due to an
+ * unknown reason
+ * @QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED: TWT session already in
+ * suspend state
+ */
+enum qca_wlan_vendor_twt_status {
+	QCA_WLAN_VENDOR_TWT_STATUS_OK = 0,
+	QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_ENABLED = 1,
+	QCA_WLAN_VENDOR_TWT_STATUS_USED_DIALOG_ID = 2,
+	QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY = 3,
+	QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST = 4,
+	QCA_WLAN_VENDOR_TWT_STATUS_NOT_SUSPENDED = 5,
+	QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM = 6,
+	QCA_WLAN_VENDOR_TWT_STATUS_NOT_READY = 7,
+	QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE = 8,
+	QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK = 9,
+	QCA_WLAN_VENDOR_TWT_STATUS_NO_RESPONSE = 10,
+	QCA_WLAN_VENDOR_TWT_STATUS_DENIED = 11,
+	QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR = 12,
+	QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED = 13,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_twt_resume - Represents attributes for
  * TWT (Target Wake Time) resume request. These attributes are sent as part of
  * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME and
- * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by
+ * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT: Optional (u8)
- * This attribute is used as the SP offset which is the offset from
- * TSF after which the wake happens. The units are in microseconds.
- * If this attribute is not provided, then the value will be set to
- * zero.
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT2_TWT: Optional (u32)
+ * These attributes are used as the SP offset which is the offset from TSF after
+ * which the wake happens. The units are in microseconds. Please note that
+ * _NEXT_TWT is limited to u8 whereas _NEXT2_TWT takes the u32 data.
+ * _NEXT2_TWT takes the precedence over _NEXT_TWT and thus the recommendation
+ * is to use _NEXT2_TWT. If neither of these attributes is provided, the value
+ * will be set to zero.
  *
  * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE: Required (u32)
  * This attribute represents the next TWT subfield size.
+ * Value 0 represents 0 bits, 1 represents 32 bits, 2 for 48 bits,
+ * and 4 for 64 bits.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID: Required (u8).
+ * Flow ID is the unique identifier for each TWT session. This attribute
+ * represents the respective TWT session to resume.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR: 6-byte MAC address
+ * Represents the MAC address of the peer to which TWT Resume is
+ * being sent. This is used in AP mode to represent the respective
+ * client and is a required parameter. In STA mode, this is an optional
+ * parameter
  */
 enum qca_wlan_vendor_attr_twt_resume {
 	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT = 1,
 	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE = 2,
+	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID = 3,
+	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT2_TWT = 4,
+	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR = 5,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST,
@@ -7389,6 +8145,28 @@
 };
 
 /**
+ * enum qca_wlan_vendor_twt_setup_resp_type - Represents the response type by
+ * the TWT responder
+ *
+ * @QCA_WLAN_VENDOR_TWT_RESP_ALTERNATE: TWT responder suggests TWT
+ * parameters that are different from TWT requesting STA suggested
+ * or demanded TWT parameters
+ * @QCA_WLAN_VENDOR_TWT_RESP_DICTATE: TWT responder demands TWT
+ * parameters that are different from TWT requesting STA TWT suggested
+ * or demanded parameters
+ * @QCA_WLAN_VENDOR_TWT_RESP_REJECT: TWT responder rejects TWT
+ * setup
+ * @QCA_WLAN_VENDOR_TWT_RESP_ACCEPT: TWT responder accepts the TWT
+ * setup.
+ */
+enum qca_wlan_vendor_twt_setup_resp_type {
+	QCA_WLAN_VENDOR_TWT_RESP_ALTERNATE = 1,
+	QCA_WLAN_VENDOR_TWT_RESP_DICTATE = 2,
+	QCA_WLAN_VENDOR_TWT_RESP_REJECT = 3,
+	QCA_WLAN_VENDOR_TWT_RESP_ACCEPT = 4,
+};
+
+/**
  * enum qca_wlan_vendor_twt_setup_req_type - Required (u8)
  * Represents the setup type being requested for TWT.
  * @QCA_WLAN_VENDOR_TWT_SETUP_REQUEST: STA is not specifying all the TWT
@@ -8321,10 +9099,14 @@
  * enum qca_wlan_vendor_attr_oem_data_params - Used by the vendor command/event
  * QCA_NL80211_VENDOR_SUBCMD_OEM_DATA.
  *
- * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA: The binary blob for the vendor
- * command/event QCA_NL80211_VENDOR_SUBCMD_OEM_DATA are carried through this
- * attribute.
- * NLA_BINARY attribute, the max size is 1024 bytes.
+ * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA: This NLA_BINARY attribute is
+ * used to set/query the data to/from the firmware. On query, the same
+ * attribute is used to carry the respective data in the reply sent by the
+ * driver to userspace. The request to set/query the data and the format of the
+ * respective data from the firmware are embedded in the attribute. The
+ * maximum size of the attribute payload is 1024 bytes.
+ * Userspace has to set the QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED
+ * attribute when the data is queried from the firmware.
  *
  * @QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO: The binary blob will be routed
  * based on this field. This optional attribute is included to specify whether
@@ -8333,11 +9115,16 @@
  * command/event.
  * This u8 attribute is used to carry information for the device type using
  * values defined by enum qca_vendor_oem_device_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED: This NLA_FLAG attribute
+ * is set when the userspace queries data from the firmware. This attribute
+ * should not be set when userspace sets the OEM data to the firmware.
  */
 enum qca_wlan_vendor_attr_oem_data_params {
 	QCA_WLAN_VENDOR_ATTR_OEM_DATA_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA = 1,
 	QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO = 2,
+	QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED = 3,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST,
@@ -8671,6 +9458,36 @@
  * disconnect reason for the last disconnection if the disconnection is
  * triggered from the host driver. The values are referred from
  * enum qca_disconnect_reason_codes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_MIC_ERROR_COUNT: u32, used in STA mode
+ * only. This represents the number of group addressed robust management frames
+ * received from this station with an invalid MIC or a missing MME when PMF is
+ * enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_REPLAY_COUNT: u32, used in STA mode
+ * only. This represents the number of group addressed robust management frames
+ * received from this station with the packet number less than or equal to the
+ * last received packet number when PMF is enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT: u32, used in STA
+ * mode only. This represents the number of Beacon frames received from this
+ * station with an invalid MIC or a missing MME when beacon protection is
+ * enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT: u32, used in STA mode
+ * only. This represents number of Beacon frames received from this station with
+ * the packet number less than or equal to the last received packet number when
+ * beacon protection is enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE: u32, used in
+ * STA mode only. The driver uses this attribute to populate the connection
+ * failure reason codes and the values are defined in
+ * enum qca_sta_connect_fail_reason_codes. Userspace applications can send
+ * QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO vendor command after receiving
+ * a connection failure indication from the driver. The driver shall not
+ * include this attribute in response to the
+ * QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO command if there is no connection
+ * failure observed in the last attempted connection.
  */
 enum qca_wlan_vendor_attr_get_sta_info {
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
@@ -8712,6 +9529,11 @@
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_IES = 36,
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PROBE_RESP_IES = 37,
 	QCA_WLAN_VENDOR_ATTR_GET_STA_DRIVER_DISCONNECT_REASON = 38,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_MIC_ERROR_COUNT = 39,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_REPLAY_COUNT = 40,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT = 41,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT = 42,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE = 43,
 
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
@@ -8841,4 +9663,352 @@
 	QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_AFTER_LAST - 1,
 };
 
+/**
+ * enum qca_wlan_tspec_operation - Operation of the config TSPEC request
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION.
+ */
+enum qca_wlan_tspec_operation {
+	QCA_WLAN_TSPEC_ADD = 0,
+	QCA_WLAN_TSPEC_DEL = 1,
+	QCA_WLAN_TSPEC_GET = 2,
+};
+
+/**
+ * enum qca_wlan_tspec_direction - Direction in TSPEC
+ * As what is defined in IEEE Std 802.11-2016, Table 9-139.
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION.
+ */
+enum qca_wlan_tspec_direction {
+	QCA_WLAN_TSPEC_DIRECTION_UPLINK = 0,
+	QCA_WLAN_TSPEC_DIRECTION_DOWNLINK = 1,
+	QCA_WLAN_TSPEC_DIRECTION_DIRECT = 2,
+	QCA_WLAN_TSPEC_DIRECTION_BOTH = 3,
+};
+
+/**
+ * enum qca_wlan_tspec_ack_policy - MAC acknowledgement policy in TSPEC
+ * As what is defined in IEEE Std 802.11-2016, Table 9-141.
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY.
+ */
+enum qca_wlan_tspec_ack_policy {
+	QCA_WLAN_TSPEC_NORMAL_ACK = 0,
+	QCA_WLAN_TSPEC_NO_ACK = 1,
+	/* Reserved */
+	QCA_WLAN_TSPEC_BLOCK_ACK = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_config_tspec - Defines attributes
+ * used by %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION:
+ * u8 attribute. Specify the TSPEC operation of this request. Possible values
+ * are defined in enum qca_wlan_tspec_operation.
+ * Mandatory attribute for all kinds of config TSPEC requests.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID:
+ * u8 attribute. TS ID. Possible values are 0-7.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD, QCA_WLAN_TSPEC_DEL,
+ * QCA_WLAN_TSPEC_GET. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION:
+ * u8 attribute. Direction of data carried by the TS. Possible values are
+ * defined in enum qca_wlan_tspec_direction.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD:
+ * Flag attribute. Indicate whether APSD is enabled for the traffic associated
+ * with the TS. set - enabled, not set - disabled.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY:
+ * u8 attribute. User priority to be used for the transport of MSDUs/A-MSDUs
+ * belonging to this TS. Possible values are 0-7.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY:
+ * u8 attribute. Indicate whether MAC acknowledgements are required for
+ * MPDUs/A-MSDUs belonging to this TS and the form of those acknowledgements.
+ * Possible values are defined in enum qca_wlan_tspec_ack_policy.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE:
+ * u16 attribute. Specify the nominal size in bytes of MSDUs/A-MSDUs
+ * belonging to this TS.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE:
+ * u16 attribute. Specify the maximum size in bytes of MSDUs/A-MSDUs
+ * belonging to this TS.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL:
+ * u32 attribute. Specify the minimum interval in microseconds between the
+ * start of two successive SPs.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL:
+ * u32 attribute. Specify the maximum interval in microseconds between the
+ * start of two successive SPs.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL:
+ * u32 attribute. Specify the minimum interval in microseconds that can elapse
+ * without arrival or transfer of an MPDU belonging to the TS before this TS
+ * is deleted by the MAC entity at the HC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL:
+ * u32 attribute. Specify the minimum interval in microseconds that can elapse
+ * without arrival or transfer of an MSDU belonging to the TS before the
+ * generation of successive QoS(+)CF-Poll is stopped for this TS. A value of
+ * 0xFFFFFFFF disables the suspension interval. The value of the suspension
+ * interval is always less than or equal to the inactivity interval.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE:
+ * u32 attribute. Indicate the lowest data rate in bps specified at the MAC
+ * SAP for transport of MSDUs or A-MSDUs belonging to this TS within the
+ * bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE:
+ * u32 attribute. Indicate the average data rate in bps specified at the MAC
+ * SAP for transport of MSDUs or A-MSDUs belonging to this TS within the
+ * bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE:
+ * u32 attribute. Indicate the maximum allowable data rate in bps specified at
+ * the MAC SAP for transport of MSDUs or A-MSDUs belonging to this TS within
+ * the bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE:
+ * u32 attribute. Specify the maximum burst size in bytes of the MSDUs/A-MSDUs
+ * belonging to this TS that arrive at the MAC SAP at the peak data rate. A
+ * value of 0 indicates that there are no bursts.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE:
+ * u32 attribute. Indicate the minimum PHY rate in bps for transport of
+ * MSDUs/A-MSDUs belonging to this TS within the bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE:
+ * u16 attribute. Specify the excess allocation of time (and bandwidth) over
+ * and above the stated application rates required to transport an MSDU/A-MSDU
+ * belonging to the TS in this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ */
+enum qca_wlan_vendor_attr_config_tspec {
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION = 1,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID = 2,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION = 3,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD = 4,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY = 5,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY = 6,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE = 7,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE = 8,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL = 9,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL = 10,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL = 11,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL = 12,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE = 13,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE = 14,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE = 15,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE = 16,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE = 17,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE = 18,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX =
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_oci_override_frame_type - OCI override frame type
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ: SA Query Request frame
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP: SA Query Response frame
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ: FT Reassociation Request
+ * frame
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ: FILS Reassociation
+ * Request frame.
+ */
+enum qca_wlan_vendor_oci_override_frame_type {
+	QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ = 1,
+	QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP = 2,
+	QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ = 3,
+	QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_oci_override: Represents attributes for
+ * OCI override request. These attributes are used inside nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE in QCA vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE: Required attribute, u8.
+ * Values from enum qca_wlan_vendor_oci_override_frame_type used in this
+ * attribute to specify the frame type in which the OCI is to be overridden.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY: Required (u32)
+ * OCI frequency (in MHz) to override in the specified frame type.
+ */
+enum qca_wlan_vendor_attr_oci_override {
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE = 1,
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_MAX =
+	QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_medium_assess_type - Type of medium assess request
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE.
+ */
+enum qca_wlan_medium_assess_type {
+	QCA_WLAN_MEDIUM_ASSESS_CCA = 0,
+	QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_medium_assess - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE:
+ * u8 attribute. Mandatory in all kinds of medium assess requests/responses.
+ * Specify the type of medium assess request and indicate its type in response.
+ * Possible values are defined in enum qca_wlan_medium_assess_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD:
+ * u32 attribute. Mandatory in CCA request.
+ * Specify the assessment period in terms of seconds. Assessment result will be
+ * sent as the response to the CCA request after the assessment period.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Total timer tick count of the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Timer tick count of idle time in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Timer tick count of Intra BSS traffic RX time in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Timer tick count of Overlapping BSS traffic RX time in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI:
+ * s32 attribute. Mandatory in response to CCA request.
+ * Maximum RSSI of Intra BSS traffic in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI:
+ * s32 attribute. Mandatory in response to CCA request.
+ * Minimum RSSI of Intra BSS traffic in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE:
+ * u8 attribute. Mandatory in congestion report request.
+ * 1-enable 0-disable.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD:
+ * u8 attribute. Mandatory in congestion report enable request and will be
+ * ignored if present in congestion report disable request. Possible values are
+ * 0-100. A vendor event QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS with the type
+ * QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT will be sent to userspace if
+ * congestion percentage reaches the configured threshold.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL:
+ * u8 attribute. Optional in congestion report enable request and will be
+ * ignored if present in congestion report disable request.
+ * Specify the interval of congestion report event in terms of seconds. Possible
+ * values are 1-255. Default value 1 will be used if this attribute is omitted
+ * or using invalid values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE:
+ * u8 attribute. Mandatory in congestion report event.
+ * Indicate the actual congestion percentage. Possible values are 0-100.
+ */
+enum qca_wlan_vendor_attr_medium_assess {
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE = 1,
+
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD = 2,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT = 3,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT = 4,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT = 5,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT = 6,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI = 7,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI = 8,
+
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE = 9,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD = 10,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL = 11,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE = 12,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX =
+	QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mbssid_tx_vdev_status - Defines attributes
+ * used by QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_VAL:
+ * u8 attribute. Notify the TX VDEV status. Possible values 0, 1
+ * belonging to MBSSID/EMA_AP configuration. 0 means Non-Tx VDEV,
+ * 1 means Tx VDEV. Mandatory attribute for all MBSSID VDEV status events.
+ */
+enum qca_wlan_vendor_attr_mbssid_tx_vdev_status {
+	QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_VAL = 1,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_MAX =
+	QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_sta_connect_fail_reason_codes - Defines values carried
+ * by QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE vendor
+ * attribute.
+ * @QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: No Probe Response frame received
+ *	for unicast Probe Request frame.
+ * @QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: STA failed to send auth request.
+ * @QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: AP didn't send ACK for
+ *	auth request.
+ * @QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: Auth response is not
+ *	received from AP.
+ * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: STA failed to send
+ *	Association Request frame.
+ * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: AP didn't send ACK for
+ *	Association Request frame.
+ * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: Association Response
+ *	frame is not received from AP.
+ */
+enum qca_sta_connect_fail_reason_codes {
+	QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND = 1,
+	QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL = 2,
+	QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED = 3,
+	QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED = 4,
+	QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL = 5,
+	QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED = 6,
+	QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED = 7,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 1b4ec6d..057e1ce 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -713,6 +713,8 @@
 		goto fail;
 	const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin);
 	x1 = crypto_bignum_init_set(bin, prime_len);
+	if (!x1)
+		goto fail;
 	debug_print_bignum("SSWU: x1 = CSEL(l, x1a, x1b)", x1, prime_len);
 
 	/* gx1 = x1^3 + a * x1 + b */
@@ -753,6 +755,8 @@
 		goto fail;
 	const_time_select_bin(is_qr, bin1, bin2, prime_len, bin);
 	v = crypto_bignum_init_set(bin, prime_len);
+	if (!v)
+		goto fail;
 	debug_print_bignum("SSWU: v = CSEL(l, gx1, gx2)", v, prime_len);
 
 	/* x = CSEL(l, x1, x2) */
@@ -1052,10 +1056,17 @@
 
 	wpa_printf(MSG_DEBUG, "SAE: Derive PT - group %d", group);
 
+	if (ssid_len > 32)
+		return NULL;
+
 	pt = os_zalloc(sizeof(*pt));
 	if (!pt)
 		return NULL;
 
+#ifdef CONFIG_SAE_PK
+	os_memcpy(pt->ssid, ssid, ssid_len);
+	pt->ssid_len = ssid_len;
+#endif /* CONFIG_SAE_PK */
 	pt->group = group;
 	pt->ec = crypto_ec_init(group);
 	if (pt->ec) {
@@ -1354,14 +1365,15 @@
 						identifier) < 0))
 		return -1;
 
-	sae->tmp->h2e = 0;
+	sae->h2e = 0;
+	sae->pk = 0;
 	return sae_derive_commit(sae);
 }
 
 
 int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
 			  const u8 *addr1, const u8 *addr2,
-			  int *rejected_groups)
+			  int *rejected_groups, const struct sae_pk *pk)
 {
 	if (!sae->tmp)
 		return -1;
@@ -1377,6 +1389,11 @@
 		return -1;
 	}
 
+#ifdef CONFIG_SAE_PK
+	os_memcpy(sae->tmp->ssid, pt->ssid, pt->ssid_len);
+	sae->tmp->ssid_len = pt->ssid_len;
+	sae->tmp->ap_pk = pk;
+#endif /* CONFIG_SAE_PK */
 	sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0;
 	wpabuf_free(sae->tmp->own_rejected_groups);
 	sae->tmp->own_rejected_groups = NULL;
@@ -1409,7 +1426,7 @@
 			return -1;
 	}
 
-	sae->tmp->h2e = 1;
+	sae->h2e = 1;
 	return sae_derive_commit(sae);
 }
 
@@ -1515,7 +1532,7 @@
 	const u8 *salt;
 	struct wpabuf *rejected_groups = NULL;
 	u8 keyseed[SAE_MAX_HASH_LEN];
-	u8 keys[SAE_MAX_HASH_LEN + SAE_PMK_LEN];
+	u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN];
 	struct crypto_bignum *tmp;
 	int ret = -1;
 	size_t hash_len, salt_len, prime_len = sae->tmp->prime_len;
@@ -1530,15 +1547,18 @@
 	 * KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK",
 	 *                      (commit-scalar + peer-commit-scalar) modulo r)
 	 * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
+	 *
+	 * When SAE-PK is used,
+	 * KCK || PMK || KEK = KDF-Hash-Length(keyseed, "SAE-PK keys", context)
 	 */
-	if (!sae->tmp->h2e)
+	if (!sae->h2e)
 		hash_len = SHA256_MAC_LEN;
 	else if (sae->tmp->dh)
 		hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
 	else
 		hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
-	if (sae->tmp->h2e && (sae->tmp->own_rejected_groups ||
-			      sae->tmp->peer_rejected_groups)) {
+	if (sae->h2e && (sae->tmp->own_rejected_groups ||
+			 sae->tmp->peer_rejected_groups)) {
 		struct wpabuf *own, *peer;
 
 		own = sae->tmp->own_rejected_groups;
@@ -1589,15 +1609,32 @@
 	 * octets). */
 	crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
 	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
-	if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
+	if (!sae->pk &&
+	    sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
 			 val, sae->tmp->order_len,
 			 keys, hash_len + SAE_PMK_LEN) < 0)
 		goto fail;
+#ifdef CONFIG_SAE_PK
+	if (sae->pk &&
+	    sae_kdf_hash(hash_len, keyseed, "SAE-PK keys",
+			 val, sae->tmp->order_len,
+			 keys, 2 * hash_len + SAE_PMK_LEN) < 0)
+		goto fail;
+#endif /* CONFIG_SAE_PK */
 	forced_memzero(keyseed, sizeof(keyseed));
 	os_memcpy(sae->tmp->kck, keys, hash_len);
 	sae->tmp->kck_len = hash_len;
 	os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN);
 	os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
+#ifdef CONFIG_SAE_PK
+	if (sae->pk) {
+		os_memcpy(sae->tmp->kek, keys + hash_len + SAE_PMK_LEN,
+			  hash_len);
+		sae->tmp->kek_len = hash_len;
+		wpa_hexdump_key(MSG_DEBUG, "SAE: KEK for SAE-PK",
+				sae->tmp->kek, sae->tmp->kek_len);
+	}
+#endif /* CONFIG_SAE_PK */
 	forced_memzero(keys, sizeof(keys));
 	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK",
 			sae->tmp->kck, sae->tmp->kck_len);
@@ -1632,7 +1669,7 @@
 		return -1;
 
 	wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
-	if (!sae->tmp->h2e && token) {
+	if (!sae->h2e && token) {
 		wpabuf_put_buf(buf, token);
 		wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token",
 			    wpabuf_head(token), wpabuf_len(token));
@@ -1673,7 +1710,7 @@
 			   identifier);
 	}
 
-	if (sae->tmp->h2e && sae->tmp->own_rejected_groups) {
+	if (sae->h2e && sae->tmp->own_rejected_groups) {
 		wpa_hexdump_buf(MSG_DEBUG, "SAE: own Rejected Groups",
 				sae->tmp->own_rejected_groups);
 		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
@@ -1683,7 +1720,7 @@
 		wpabuf_put_buf(buf, sae->tmp->own_rejected_groups);
 	}
 
-	if (sae->tmp->h2e && token) {
+	if (sae->h2e && token) {
 		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
 		wpabuf_put_u8(buf, 1 + wpabuf_len(token));
 		wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
@@ -2189,13 +2226,14 @@
 }
 
 
-void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
+int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
 {
 	const u8 *sc;
 	size_t hash_len;
+	int res;
 
 	if (sae->tmp == NULL)
-		return;
+		return -1;
 
 	hash_len = sae->tmp->kck_len;
 
@@ -2206,17 +2244,26 @@
 		sae->send_confirm++;
 
 	if (sae->tmp->ec)
-		sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
-				   sae->tmp->own_commit_element_ecc,
-				   sae->peer_commit_scalar,
-				   sae->tmp->peer_commit_element_ecc,
-				   wpabuf_put(buf, hash_len));
+		res = sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
+					 sae->tmp->own_commit_element_ecc,
+					 sae->peer_commit_scalar,
+					 sae->tmp->peer_commit_element_ecc,
+					 wpabuf_put(buf, hash_len));
 	else
-		sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
-				   sae->tmp->own_commit_element_ffc,
-				   sae->peer_commit_scalar,
-				   sae->tmp->peer_commit_element_ffc,
-				   wpabuf_put(buf, hash_len));
+		res = sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
+					 sae->tmp->own_commit_element_ffc,
+					 sae->peer_commit_scalar,
+					 sae->tmp->peer_commit_element_ffc,
+					 wpabuf_put(buf, hash_len));
+	if (res)
+		return res;
+
+#ifdef CONFIG_SAE_PK
+	if (sae_write_confirm_pk(sae, buf) < 0)
+		return -1;
+#endif /* CONFIG_SAE_PK */
+
+	return 0;
 }
 
 
@@ -2270,6 +2317,12 @@
 		return -1;
 	}
 
+#ifdef CONFIG_SAE_PK
+	if (sae_check_confirm_pk(sae, data + 2 + hash_len,
+				 len - 2 - hash_len) < 0)
+		return -1;
+#endif /* CONFIG_SAE_PK */
+
 	return 0;
 }
 
diff --git a/src/common/sae.h b/src/common/sae.h
index 7966d70..2243c0f 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -16,11 +16,27 @@
 #define SAE_MAX_ECC_PRIME_LEN 66
 #define SAE_MAX_HASH_LEN 64
 #define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN + 255)
+#ifdef CONFIG_SAE_PK
+#define SAE_CONFIRM_MAX_LEN ((2 + SAE_MAX_HASH_LEN) + 1500)
+#else /* CONFIG_SAE_PK */
 #define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_HASH_LEN)
+#endif /* CONFIG_SAE_PK */
+#define SAE_PK_M_LEN 16
 
 /* Special value returned by sae_parse_commit() */
 #define SAE_SILENTLY_DISCARD 65535
 
+struct sae_pk {
+	struct wpabuf *m;
+	struct crypto_ec_key *key;
+	int group;
+	struct wpabuf *pubkey; /* DER encoded subjectPublicKey */
+#ifdef CONFIG_TESTING_OPTIONS
+	struct crypto_ec_key *sign_key_override;
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+
 struct sae_temporary_data {
 	u8 kck[SAE_MAX_HASH_LEN];
 	size_t kck_len;
@@ -46,8 +62,25 @@
 	u8 bssid[ETH_ALEN];
 	struct wpabuf *own_rejected_groups;
 	struct wpabuf *peer_rejected_groups;
-	unsigned int h2e:1;
 	unsigned int own_addr_higher:1;
+
+#ifdef CONFIG_SAE_PK
+	u8 kek[SAE_MAX_HASH_LEN];
+	size_t kek_len;
+	const struct sae_pk *ap_pk;
+	u8 own_addr[ETH_ALEN];
+	u8 peer_addr[ETH_ALEN];
+	u8 fingerprint[SAE_MAX_HASH_LEN];
+	size_t fingerprint_bytes;
+	size_t fingerprint_bits;
+	size_t lambda;
+	unsigned int sec;
+	u8 ssid[32];
+	size_t ssid_len;
+#ifdef CONFIG_TESTING_OPTIONS
+	bool omit_pk_elem;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_SAE_PK */
 };
 
 struct sae_pt {
@@ -58,6 +91,10 @@
 
 	const struct dh_group *dh;
 	struct crypto_bignum *ffc_pt;
+#ifdef CONFIG_SAE_PK
+	u8 ssid[32];
+	size_t ssid_len;
+#endif /* CONFIG_SAE_PK */
 };
 
 enum sae_state {
@@ -74,6 +111,8 @@
 	int group;
 	unsigned int sync; /* protocol instance variable: Sync */
 	u16 rc; /* protocol instance variable: Rc (received send-confirm) */
+	unsigned int h2e:1;
+	unsigned int pk:1;
 	struct sae_temporary_data *tmp;
 };
 
@@ -86,17 +125,19 @@
 		       const char *identifier, struct sae_data *sae);
 int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
 			  const u8 *addr1, const u8 *addr2,
-			  int *rejected_groups);
+			  int *rejected_groups, const struct sae_pk *pk);
 int sae_process_commit(struct sae_data *sae);
 int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
 		     const struct wpabuf *token, const char *identifier);
 u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
 		     const u8 **token, size_t *token_len, int *allowed_groups,
 		     int h2e);
-void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
+int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
 int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
 u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
 const char * sae_state_txt(enum sae_state state);
+size_t sae_ecc_prime_len_2_hash_len(size_t prime_len);
+size_t sae_ffc_prime_len_2_hash_len(size_t prime_len);
 struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
 			      const u8 *password, size_t password_len,
 			      const char *identifier);
@@ -108,4 +149,24 @@
 			   const u8 *addr1, const u8 *addr2);
 void sae_deinit_pt(struct sae_pt *pt);
 
+/* sae_pk.c */
+#ifdef CONFIG_SAE_PK
+bool sae_pk_valid_password(const char *pw);
+#else /* CONFIG_SAE_PK */
+static inline bool sae_pk_valid_password(const char *pw)
+{
+	return false;
+}
+#endif /* CONFIG_SAE_PK */
+char * sae_pk_base32_encode(const u8 *src, size_t len_bits);
+u8 * sae_pk_base32_decode(const char *src, size_t len, size_t *out_len);
+int sae_pk_set_password(struct sae_data *sae, const char *password);
+void sae_deinit_pk(struct sae_pk *pk);
+struct sae_pk * sae_parse_pk(const char *val);
+int sae_write_confirm_pk(struct sae_data *sae, struct wpabuf *buf);
+int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len);
+int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash);
+u32 sae_pk_get_be19(const u8 *buf);
+void sae_pk_buf_shift_left_19(u8 *buf, size_t len);
+
 #endif /* SAE_H */
diff --git a/src/common/sae_pk.c b/src/common/sae_pk.c
new file mode 100644
index 0000000..df79e5f
--- /dev/null
+++ b/src/common/sae_pk.c
@@ -0,0 +1,884 @@
+/*
+ * SAE-PK
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <stdint.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/crypto.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "sae.h"
+
+
+/* RFC 4648 base 32 alphabet with lowercase characters */
+static const char *sae_pk_base32_table = "abcdefghijklmnopqrstuvwxyz234567";
+
+
+static const u8 d_mult_table[] = {
+	 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+	16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+	 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,
+	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16,
+	 2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,
+	18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17,
+	 3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,
+	19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18,
+	 4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,
+	20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19,
+	 5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,
+	21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20,
+	 6,  7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,
+	22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21,
+	 7,  8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,
+	23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
+	 8,  9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,
+	24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23,
+	 9, 10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,
+	25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+	10, 11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
+	26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+	11, 12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
+	27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+	12, 13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
+	28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+	13, 14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
+	29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+	14, 15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13,
+	30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+	15,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+	31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+	16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17,
+	 0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,
+	17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18,
+	 1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,
+	18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19,
+	 2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,
+	19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20,
+	 3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,
+	20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
+	 4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,
+	21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22,
+	 5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,
+	22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23,
+	 6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,  7,
+	23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24,
+	 7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,  8,
+	24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25,
+	 8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,  9,
+	25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26,
+	 9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11, 10,
+	26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27,
+	10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12, 11,
+	27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28,
+	11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13, 12,
+	28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29,
+	12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14, 13,
+	29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30,
+	13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15, 14,
+	30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31,
+	14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0, 15,
+	31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
+	15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0
+};
+
+static const u8 d_perm_table[] = {
+	 7,  2,  1, 30, 16, 20, 27, 11, 31,  6,  8, 13, 29,  5, 10, 21,
+	22,  3, 24,  0, 23, 25, 12,  9, 28, 14,  4, 15, 17, 18, 19, 26
+};
+
+
+static u8 d_permute(u8 val, unsigned int iter)
+{
+	if (iter == 0)
+		return val;
+	return d_permute(d_perm_table[val], iter - 1);
+}
+
+
+static u8 d_invert(u8 val)
+{
+	if (val > 0 && val < 16)
+		return 16 - val;
+	return val;
+}
+
+
+static char d_check_char(const char *str, size_t len)
+{
+	size_t i;
+	u8 val = 0;
+	u8 dtable[256];
+	unsigned int iter = 1;
+	int j;
+
+	os_memset(dtable, 0x80, 256);
+	for (i = 0; sae_pk_base32_table[i]; i++)
+		dtable[(u8) sae_pk_base32_table[i]] = i;
+
+	for (j = len - 1; j >= 0; j--) {
+		u8 c, p;
+
+		c = dtable[(u8) str[j]];
+		if (c == 0x80)
+			continue;
+		p = d_permute(c, iter);
+		iter++;
+		val = d_mult_table[val * 32 + p];
+	}
+
+	return sae_pk_base32_table[d_invert(val)];
+}
+
+
+bool sae_pk_valid_password(const char *pw)
+{
+	int pos;
+	size_t i, pw_len = os_strlen(pw);
+	u8 sec_1b;
+	u8 dtable[256];
+
+	os_memset(dtable, 0x80, 256);
+	for (i = 0; sae_pk_base32_table[i]; i++)
+		dtable[(u8) sae_pk_base32_table[i]] = i;
+
+	/* SAE-PK password has at least three four character components
+	 * separated by hyphens. */
+	if (pw_len < 14 || pw_len % 5 != 4) {
+		wpa_printf(MSG_DEBUG, "SAE-PK: Not a valid password (length)");
+		return false;
+	}
+
+	for (pos = 0; pw[pos]; pos++) {
+		if (pos && pos % 5 == 4) {
+			if (pw[pos] != '-') {
+				wpa_printf(MSG_DEBUG,
+					   "SAE-PK: Not a valid password (separator)");
+				return false;
+			}
+			continue;
+		}
+		if (dtable[(u8) pw[pos]] == 0x80) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE-PK: Not a valid password (character)");
+			return false;
+		}
+	}
+
+	/* Verify that the checksum character is valid */
+	if (pw[pw_len - 1] != d_check_char(pw, pw_len - 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE-PK: Not a valid password (checksum)");
+		return false;
+	}
+
+	/* Verify that Sec_1b bits match */
+	sec_1b = dtable[(u8) pw[0]] & BIT(4);
+	for (i = 5; i < pw_len; i += 5) {
+		if (sec_1b != (dtable[(u8) pw[i]] & BIT(4))) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE-PK: Not a valid password (Sec_1b)");
+			return false;
+		}
+	}
+	return true;
+}
+
+
+static char * add_char(const char *start, char *pos, u8 idx, size_t *bits)
+{
+	if (*bits == 0)
+		return pos;
+	if (*bits > 5)
+		*bits -= 5;
+	else
+		*bits = 0;
+
+	if ((pos - start) % 5 == 4)
+		*pos++ = '-';
+	*pos++ = sae_pk_base32_table[idx];
+	return pos;
+}
+
+
+/* Base32 encode a password and add hyper separators and checksum */
+char * sae_pk_base32_encode(const u8 *src, size_t len_bits)
+{
+	char *out, *pos;
+	size_t olen, extra_pad, i;
+	u64 block = 0;
+	u8 val;
+	size_t len = (len_bits + 7) / 8;
+	size_t left = len_bits;
+	int j;
+
+	if (len == 0 || len >= SIZE_MAX / 8)
+		return NULL;
+	olen = len * 8 / 5 + 1;
+	olen += olen / 4; /* hyphen separators */
+	pos = out = os_zalloc(olen + 2); /* include room for ChkSum and nul */
+	if (!out)
+		return NULL;
+
+	extra_pad = (5 - len % 5) % 5;
+	for (i = 0; i < len + extra_pad; i++) {
+		val = i < len ? src[i] : 0;
+		block <<= 8;
+		block |= val;
+		if (i % 5 == 4) {
+			for (j = 7; j >= 0; j--)
+				pos = add_char(out, pos,
+					       (block >> j * 5) & 0x1f, &left);
+			block = 0;
+		}
+	}
+
+	*pos = d_check_char(out, os_strlen(out));
+
+	return out;
+}
+
+
+u8 * sae_pk_base32_decode(const char *src, size_t len, size_t *out_len)
+{
+	u8 dtable[256], *out, *pos, tmp;
+	u64 block = 0;
+	size_t i, count, olen;
+	int pad = 0;
+	size_t extra_pad;
+
+	os_memset(dtable, 0x80, 256);
+	for (i = 0; sae_pk_base32_table[i]; i++)
+		dtable[(u8) sae_pk_base32_table[i]] = i;
+	dtable['='] = 0;
+
+	count = 0;
+	for (i = 0; i < len; i++) {
+		if (dtable[(u8) src[i]] != 0x80)
+			count++;
+	}
+
+	if (count == 0)
+		return NULL;
+	extra_pad = (8 - count % 8) % 8;
+
+	olen = (count + extra_pad) / 8 * 5;
+	pos = out = os_malloc(olen);
+	if (!out)
+		return NULL;
+
+	count = 0;
+	for (i = 0; i < len + extra_pad; i++) {
+		u8 val;
+
+		if (i >= len)
+			val = '=';
+		else
+			val = src[i];
+		tmp = dtable[val];
+		if (tmp == 0x80)
+			continue;
+
+		if (val == '=')
+			pad++;
+		block <<= 5;
+		block |= tmp;
+		count++;
+		if (count == 8) {
+			*pos++ = (block >> 32) & 0xff;
+			*pos++ = (block >> 24) & 0xff;
+			*pos++ = (block >> 16) & 0xff;
+			*pos++ = (block >> 8) & 0xff;
+			*pos++ = block & 0xff;
+			count = 0;
+			block = 0;
+			if (pad) {
+				/* Leave in all the available bits with zero
+				 * padding to full octets from right. */
+				pos -= pad * 5 / 8;
+				break;
+			}
+		}
+	}
+
+	*out_len = pos - out;
+	return out;
+}
+
+
+u32 sae_pk_get_be19(const u8 *buf)
+{
+	return (buf[0] << 11) | (buf[1] << 3) | (buf[2] >> 5);
+}
+
+
+/* shift left by two octets and three bits; fill in zeros from right;
+ * len must be at least three */
+void sae_pk_buf_shift_left_19(u8 *buf, size_t len)
+{
+	u8 *dst, *src, *end;
+
+	dst = buf;
+	src = buf + 2;
+	end = buf + len;
+
+	while (src + 1 < end) {
+		*dst++ = (src[0] << 3) | (src[1] >> 5);
+		src++;
+	}
+	*dst++ = *src << 3;
+	*dst++ = 0;
+	*dst++ = 0;
+}
+
+
+static void sae_pk_buf_shift_left_1(u8 *buf, size_t len)
+{
+	u8 *dst, *src, *end;
+
+	dst = buf;
+	src = buf;
+	end = buf + len;
+
+	while (src + 1 < end) {
+		*dst++ = (src[0] << 1) | (src[1] >> 7);
+		src++;
+	}
+	*dst++ = *src << 1;
+}
+
+
+int sae_pk_set_password(struct sae_data *sae, const char *password)
+{
+	struct sae_temporary_data *tmp = sae->tmp;
+	size_t len, pw_len;
+	u8 *pw, *pos;
+	int bits;
+	u32 val = 0, val19;
+	unsigned int val_bits = 0;
+
+	if (!tmp)
+		return -1;
+
+	os_memset(tmp->fingerprint, 0, sizeof(tmp->fingerprint));
+	tmp->fingerprint_bytes = tmp->fingerprint_bits = 0;
+
+	len = os_strlen(password);
+	if (len < 1 || !sae_pk_valid_password(password))
+		return -1;
+
+	pw = sae_pk_base32_decode(password, len, &pw_len);
+	if (!pw)
+		return -1;
+
+	tmp->sec = (pw[0] & BIT(7)) ? 3 : 5;
+	tmp->lambda = len - len / 5;
+	tmp->fingerprint_bits = 8 * tmp->sec + 19 * tmp->lambda / 4 - 5;
+	wpa_printf(MSG_DEBUG, "SAE-PK: Sec=%u Lambda=%zu fingerprint_bits=%zu",
+		   tmp->sec, tmp->lambda, tmp->fingerprint_bits);
+
+	/* Construct Fingerprint from PasswordBase by prefixing with Sec zero
+	 * octets and skipping the Sec_1b bits */
+	pos = &tmp->fingerprint[tmp->sec];
+	bits = tmp->fingerprint_bits - 8 * tmp->sec;
+	wpa_hexdump_key(MSG_DEBUG, "SAE-PK: PasswordBase", pw, pw_len);
+	while (bits > 0) {
+		if (val_bits < 8) {
+			sae_pk_buf_shift_left_1(pw, pw_len); /* Sec_1b */
+			val19 = sae_pk_get_be19(pw);
+			sae_pk_buf_shift_left_19(pw, pw_len);
+			val = (val << 19) | val19;
+			val_bits += 19;
+		}
+		if (val_bits >= 8) {
+			if (bits < 8)
+				break;
+			*pos++ = (val >> (val_bits - 8)) & 0xff;
+			val_bits -= 8;
+			bits -= 8;
+		}
+	}
+	if (bits > 0) {
+		val >>= val_bits - bits;
+		*pos++ = val << (8 - bits);
+	}
+	tmp->fingerprint_bytes = pos - tmp->fingerprint;
+	wpa_hexdump_key(MSG_DEBUG, "SAE-PK: Fingerprint",
+			tmp->fingerprint, tmp->fingerprint_bytes);
+	bin_clear_free(pw, pw_len);
+	return 0;
+}
+
+
+static size_t sae_group_2_hash_len(int group)
+{
+	switch (group) {
+	case 19:
+		return 32;
+	case 20:
+		return 48;
+	case 21:
+		return 64;
+	}
+
+	return 0;
+}
+
+
+void sae_deinit_pk(struct sae_pk *pk)
+{
+	if (pk) {
+		wpabuf_free(pk->m);
+		crypto_ec_key_deinit(pk->key);
+#ifdef CONFIG_TESTING_OPTIONS
+		crypto_ec_key_deinit(pk->sign_key_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+		wpabuf_free(pk->pubkey);
+		os_free(pk);
+	}
+}
+
+
+struct sae_pk * sae_parse_pk(const char *val)
+{
+	struct sae_pk *pk;
+	const char *pos;
+#ifdef CONFIG_TESTING_OPTIONS
+	const char *pos2;
+#endif /* CONFIG_TESTING_OPTIONS */
+	size_t len;
+	unsigned char *der;
+	size_t der_len, b_len;
+
+	/* <m-as-hexdump>:<base64-encoded-DER-encoded-key> */
+
+	pos = os_strchr(val, ':');
+	if (!pos || (pos - val) & 0x01)
+		return NULL;
+	len = (pos - val) / 2;
+	if (len != SAE_PK_M_LEN) {
+		wpa_printf(MSG_INFO, "SAE: Unexpected Modifier M length %zu",
+			   len);
+		return NULL;
+	}
+
+	pk = os_zalloc(sizeof(*pk));
+	if (!pk)
+		return NULL;
+	pk->m = wpabuf_alloc(len);
+	if (!pk->m || hexstr2bin(val, wpabuf_put(pk->m, len), len)) {
+		wpa_printf(MSG_INFO, "SAE: Failed to parse m");
+		goto fail;
+	}
+
+	pos++;
+	b_len = os_strlen(pos);
+#ifdef CONFIG_TESTING_OPTIONS
+	pos2 = os_strchr(pos, ':');
+	if (pos2) {
+		b_len = pos2 - pos;
+		pos2++;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	der = base64_decode(pos, b_len, &der_len);
+	if (!der) {
+		wpa_printf(MSG_INFO, "SAE: Failed to base64 decode PK key");
+		goto fail;
+	}
+
+	pk->key = crypto_ec_key_parse_priv(der, der_len);
+	bin_clear_free(der, der_len);
+	if (!pk->key)
+		goto fail;
+	pk->group = crypto_ec_key_group(pk->key);
+	pk->pubkey = crypto_ec_key_get_subject_public_key(pk->key);
+	if (!pk->pubkey)
+		goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (pos2) {
+		der = base64_decode(pos2, os_strlen(pos2), &der_len);
+		if (!der) {
+			wpa_printf(MSG_INFO,
+				   "SAE: Failed to base64 decode PK key");
+			goto fail;
+		}
+
+		pk->sign_key_override = crypto_ec_key_parse_priv(der, der_len);
+		bin_clear_free(der, der_len);
+		if (!pk->sign_key_override)
+			goto fail;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	return pk;
+fail:
+	sae_deinit_pk(pk);
+	return NULL;
+}
+
+
+int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash)
+{
+	if (hash_len == 32)
+		return sha256_vector(1, &data, &len, hash);
+#ifdef CONFIG_SHA384
+	if (hash_len == 48)
+		return sha384_vector(1, &data, &len, hash);
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+	if (hash_len == 64)
+		return sha512_vector(1, &data, &len, hash);
+#endif /* CONFIG_SHA512 */
+	return -1;
+}
+
+
+static int sae_pk_hash_sig_data(struct sae_data *sae, size_t hash_len,
+				bool ap, const u8 *m, size_t m_len,
+				const u8 *pubkey, size_t pubkey_len, u8 *hash)
+{
+	struct sae_temporary_data *tmp = sae->tmp;
+	struct wpabuf *sig_data;
+	u8 *pos;
+	int ret = -1;
+
+	/* Signed data for KeyAuth: eleAP || eleSTA || scaAP || scaSTA ||
+	 * M || K_AP || AP-BSSID || STA-MAC */
+	sig_data = wpabuf_alloc(tmp->prime_len * 6 + m_len + pubkey_len +
+				2 * ETH_ALEN);
+	if (!sig_data)
+		goto fail;
+	pos = wpabuf_put(sig_data, 2 * tmp->prime_len);
+	if (crypto_ec_point_to_bin(tmp->ec, ap ? tmp->own_commit_element_ecc :
+				   tmp->peer_commit_element_ecc,
+				   pos, pos + tmp->prime_len) < 0)
+		goto fail;
+	pos = wpabuf_put(sig_data, 2 * tmp->prime_len);
+	if (crypto_ec_point_to_bin(tmp->ec, ap ? tmp->peer_commit_element_ecc :
+				   tmp->own_commit_element_ecc,
+				   pos, pos + tmp->prime_len) < 0)
+		goto fail;
+	if (crypto_bignum_to_bin(ap ? tmp->own_commit_scalar :
+				 sae->peer_commit_scalar,
+				 wpabuf_put(sig_data, tmp->prime_len),
+				 tmp->prime_len, tmp->prime_len) < 0 ||
+	    crypto_bignum_to_bin(ap ? sae->peer_commit_scalar :
+				 tmp->own_commit_scalar,
+				 wpabuf_put(sig_data, tmp->prime_len),
+				 tmp->prime_len, tmp->prime_len) < 0)
+		goto fail;
+	wpabuf_put_data(sig_data, m, m_len);
+	wpabuf_put_data(sig_data, pubkey, pubkey_len);
+	wpabuf_put_data(sig_data, ap ? tmp->own_addr : tmp->peer_addr,
+			ETH_ALEN);
+	wpabuf_put_data(sig_data, ap ? tmp->peer_addr : tmp->own_addr,
+			ETH_ALEN);
+	wpa_hexdump_buf_key(MSG_DEBUG, "SAE-PK: Data to be signed for KeyAuth",
+			    sig_data);
+	if (sae_hash(hash_len, wpabuf_head(sig_data), wpabuf_len(sig_data),
+		     hash) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: hash(data to be signed)",
+		    hash, hash_len);
+	ret = 0;
+fail:
+	wpabuf_free(sig_data);
+	return ret;
+}
+
+
+int sae_write_confirm_pk(struct sae_data *sae, struct wpabuf *buf)
+{
+	struct sae_temporary_data *tmp = sae->tmp;
+	struct wpabuf *sig = NULL;
+	size_t need;
+	int ret = -1;
+	u8 *encr_mod;
+	size_t encr_mod_len;
+	const struct sae_pk *pk;
+	u8 hash[SAE_MAX_HASH_LEN];
+	size_t hash_len;
+	struct crypto_ec_key *key;
+
+	if (!tmp)
+		return -1;
+
+	pk = tmp->ap_pk;
+	if (!sae->pk || !pk)
+		return 0;
+
+	key = pk->key;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (tmp->omit_pk_elem)
+		return 0;
+	if (pk->sign_key_override) {
+		wpa_printf(MSG_INFO, "TESTING: Override SAE-PK signing key");
+		key = pk->sign_key_override;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (tmp->kek_len != 32 && tmp->kek_len != 48 && tmp->kek_len != 64) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: No KEK available for writing confirm");
+		return -1;
+	}
+
+	if (!tmp->ec) {
+		/* Only ECC groups are supported for SAE-PK in the current
+		 * implementation. */
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: SAE commit did not use an ECC group");
+		return -1;
+	}
+
+	hash_len = sae_group_2_hash_len(pk->group);
+	if (sae_pk_hash_sig_data(sae, hash_len, true, wpabuf_head(pk->m),
+				 wpabuf_len(pk->m), wpabuf_head(pk->pubkey),
+				 wpabuf_len(pk->pubkey), hash) < 0)
+		goto fail;
+	sig = crypto_ec_key_sign(key, hash, hash_len);
+	if (!sig)
+		goto fail;
+	wpa_hexdump_buf(MSG_DEBUG, "SAE-PK: KeyAuth = Sig_AP()", sig);
+
+	/* TODO: fragmentation if any of the elements needs it for a group
+	 * using sufficiently large primes (none of the currently supported
+	 * ones do) */
+
+	encr_mod_len = wpabuf_len(pk->m) + AES_BLOCK_SIZE;
+	need = 4 + wpabuf_len(pk->pubkey) + 3 + wpabuf_len(sig) +
+		6 + encr_mod_len;
+	if (wpabuf_tailroom(buf) < need) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: No room in message buffer for SAE-PK elements (%zu < %zu)",
+			   wpabuf_tailroom(buf), need);
+		goto fail;
+	}
+
+	/* FILS Public Key element */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 2 + wpabuf_len(pk->pubkey));
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_PUBLIC_KEY);
+	wpabuf_put_u8(buf, 2); /* Key Type: ECDSA public key */
+	wpabuf_put_buf(buf, pk->pubkey);
+
+	/* FILS Key Confirmation element (KeyAuth) */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 1 + wpabuf_len(sig));
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+	/* KeyAuth = Sig_AP(eleAP || eleSTA || scaAP || scaSTA || M || K_AP ||
+	 *                  AP-BSSID || STA-MAC) */
+	wpabuf_put_buf(buf, sig);
+
+	/* SAE-PK element */
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 4 + encr_mod_len);
+	wpabuf_put_be32(buf, SAE_PK_IE_VENDOR_TYPE);
+	/* EncryptedModifier = AES-SIV-Q(M); no AAD */
+	encr_mod = wpabuf_put(buf, encr_mod_len);
+	if (aes_siv_encrypt(tmp->kek, tmp->kek_len,
+			    wpabuf_head(pk->m), wpabuf_len(pk->m),
+			    0, NULL, NULL, encr_mod) < 0)
+		goto fail;
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: EncryptedModifier",
+		    encr_mod, encr_mod_len);
+
+	ret = 0;
+fail:
+	wpabuf_free(sig);
+	return ret;
+
+}
+
+
+static bool sae_pk_valid_fingerprint(struct sae_data *sae,
+				     const u8 *m, size_t m_len,
+				     const u8 *k_ap, size_t k_ap_len, int group)
+{
+	struct sae_temporary_data *tmp = sae->tmp;
+	u8 *hash_data, *pos;
+	size_t hash_len, hash_data_len;
+	u8 hash[SAE_MAX_HASH_LEN];
+	int res;
+
+	if (!tmp->fingerprint_bytes) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE-PK: No PW available for K_AP fingerprint check");
+		return false;
+	}
+
+	/* Fingerprint = L(Hash(SSID || M || K_AP), 0, 8*Sec + 19*Lambda/4 - 5)
+	 */
+
+	hash_len = sae_group_2_hash_len(group);
+	hash_data_len = tmp->ssid_len + m_len + k_ap_len;
+	hash_data = os_malloc(hash_data_len);
+	if (!hash_data)
+		return false;
+	pos = hash_data;
+	os_memcpy(pos, tmp->ssid, tmp->ssid_len);
+	pos += tmp->ssid_len;
+	os_memcpy(pos, m, m_len);
+	pos += m_len;
+	os_memcpy(pos, k_ap, k_ap_len);
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE-PK: SSID || M || K_AP",
+			hash_data, hash_data_len);
+	res = sae_hash(hash_len, hash_data, hash_data_len, hash);
+	bin_clear_free(hash_data, hash_data_len);
+	if (res < 0)
+		return false;
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Hash(SSID || M || K_AP)",
+		    hash, hash_len);
+
+	if (tmp->fingerprint_bits > hash_len * 8) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: Not enough hash output bits for the fingerprint");
+		return false;
+	}
+	if (tmp->fingerprint_bits % 8) {
+		size_t extra;
+
+		/* Zero out the extra bits in the last octet */
+		extra = 8 - tmp->fingerprint_bits % 8;
+		pos = &hash[tmp->fingerprint_bits / 8];
+		*pos = (*pos >> extra) << extra;
+	}
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Fingerprint", hash,
+		    tmp->fingerprint_bytes);
+	res = os_memcmp_const(hash, tmp->fingerprint, tmp->fingerprint_bytes);
+	if (res) {
+		wpa_printf(MSG_DEBUG, "SAE-PK: K_AP fingerprint mismatch");
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Expected fingerprint",
+		    tmp->fingerprint, tmp->fingerprint_bytes);
+		return false;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE-PK: Valid K_AP fingerprint");
+	return true;
+}
+
+
+int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len)
+{
+	struct sae_temporary_data *tmp = sae->tmp;
+	const u8 *k_ap;
+	u8 m[SAE_PK_M_LEN];
+	size_t k_ap_len;
+	struct crypto_ec_key *key;
+	int res;
+	u8 hash[SAE_MAX_HASH_LEN];
+	size_t hash_len;
+	int group;
+	struct ieee802_11_elems elems;
+
+	if (!tmp)
+		return -1;
+	if (!sae->pk || tmp->ap_pk)
+		return 0;
+
+	if (tmp->kek_len != 32 && tmp->kek_len != 48 && tmp->kek_len != 64) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: No KEK available for checking confirm");
+		return -1;
+	}
+
+	if (!tmp->ec) {
+		/* Only ECC groups are supported for SAE-PK in the current
+		 * implementation. */
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: SAE commit did not use an ECC group");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Received confirm IEs", ies, ies_len);
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_INFO, "SAE-PK: Failed to parse confirm IEs");
+		return -1;
+	}
+	if (!elems.fils_pk || !elems.fils_key_confirm || !elems.sae_pk) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: Not all mandatory IEs included in confirm");
+		return -1;
+	}
+
+	/* TODO: Fragment reassembly */
+
+	if (elems.sae_pk_len < SAE_PK_M_LEN + AES_BLOCK_SIZE) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: No room for EncryptedModifier in SAE-PK element");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: EncryptedModifier",
+		    elems.sae_pk, SAE_PK_M_LEN + AES_BLOCK_SIZE);
+
+	if (aes_siv_decrypt(tmp->kek, tmp->kek_len,
+			    elems.sae_pk, SAE_PK_M_LEN + AES_BLOCK_SIZE,
+			    0, NULL, NULL, m) < 0) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: Failed to decrypt EncryptedModifier");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "SAE-PK: Modifier M", m, SAE_PK_M_LEN);
+
+	if (elems.fils_pk[0] != 2) {
+		wpa_printf(MSG_INFO, "SAE-PK: Unsupported public key type %u",
+			   elems.fils_pk[0]);
+		return -1;
+	}
+	k_ap_len = elems.fils_pk_len - 1;
+	k_ap = elems.fils_pk + 1;
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Received K_AP", k_ap, k_ap_len);
+	/* TODO: Check against the public key, if one is stored in the network
+	 * profile */
+
+	key = crypto_ec_key_parse_pub(k_ap, k_ap_len);
+	if (!key) {
+		wpa_printf(MSG_INFO, "SAE-PK: Failed to parse K_AP");
+		return -1;
+	}
+
+	group = crypto_ec_key_group(key);
+	if (!sae_pk_valid_fingerprint(sae, m, SAE_PK_M_LEN, k_ap, k_ap_len,
+				      group)) {
+		crypto_ec_key_deinit(key);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SAE-PK: Received KeyAuth",
+		    elems.fils_key_confirm, elems.fils_key_confirm_len);
+
+	hash_len = sae_group_2_hash_len(group);
+	if (sae_pk_hash_sig_data(sae, hash_len, false, m, SAE_PK_M_LEN,
+				 k_ap, k_ap_len, hash) < 0) {
+		crypto_ec_key_deinit(key);
+		return -1;
+	}
+
+	res = crypto_ec_key_verify_signature(key, hash, hash_len,
+					     elems.fils_key_confirm,
+					     elems.fils_key_confirm_len);
+	crypto_ec_key_deinit(key);
+
+	if (res != 1) {
+		wpa_printf(MSG_INFO,
+			   "SAE-PK: Invalid or incorrect signature in KeyAuth");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE-PK: Valid KeyAuth signature received");
+
+	/* TODO: Store validated public key into network profile */
+
+	return 0;
+}
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 1e7498a..82a5a17 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1633,7 +1633,8 @@
 	if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0)
 		return -1;
 	os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
-	os_memset(r0_key_data, 0, sizeof(r0_key_data));
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
+	forced_memzero(r0_key_data, sizeof(r0_key_data));
 	return 0;
 }
 
@@ -1670,6 +1671,7 @@
 	if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0)
 		return -1;
 	os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
 	return 0;
 }
 
@@ -1839,7 +1841,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
 	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
 
-	os_memset(tmp, 0, sizeof(tmp));
+	forced_memzero(tmp, sizeof(tmp));
 
 	return 0;
 }
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 354de28..acc2d6c 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -97,6 +97,10 @@
 	"CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
 /** Unprotected Beacon frame dropped */
 #define WPA_EVENT_UNPROT_BEACON "CTRL-EVENT-UNPROT-BEACON "
+/** Decision made to do a within-ESS roam */
+#define WPA_EVENT_DO_ROAM "CTRL-EVENT-DO-ROAM "
+/** Decision made to skip a within-ESS roam */
+#define WPA_EVENT_SKIP_ROAM "CTRL-EVENT-SKIP-ROAM "
 
 /** IP subnet status change notification
  *
@@ -122,6 +126,8 @@
 #define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
 /** Frequency ranges that the driver recommends to avoid */
 #define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
+/** Result of MSCS setup */
+#define WPA_EVENT_MSCS_RESULT "CTRL-EVENT-MSCS-RESULT "
 /** WPS overlap detected in PBC mode */
 #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
 /** Available WPS AP with active PBC found in scan results */
@@ -178,7 +184,11 @@
 #define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
 #define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
 #define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY "
+#define DPP_EVENT_PP_KEY "DPP-PP-KEY "
 #define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY "
+#define DPP_EVENT_SERVER_NAME "DPP-SERVER-NAME "
+#define DPP_EVENT_CERTBAG "DPP-CERTBAG "
+#define DPP_EVENT_CACERT "DPP-CACERT "
 #define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR "
 #define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID "
 #define DPP_EVENT_CONFIGURATOR_ID "DPP-CONFIGURATOR-ID "
@@ -192,6 +202,8 @@
 #define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED "
 #define DPP_EVENT_MUD_URL "DPP-MUD-URL "
 #define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT "
+#define DPP_EVENT_CSR "DPP-CSR "
+#define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX "
 
 /* MESH events */
 #define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
@@ -388,6 +400,10 @@
 /* Transition mode disabled indication - followed by bitmap */
 #define TRANSITION_DISABLE "TRANSITION-DISABLE "
 
+/* OCV validation failure; parameters: addr=<src addr>
+ * frame=<saqueryreq/saqueryresp> error=<error string> */
+#define OCV_FAILURE "OCV-FAILURE "
+
 #ifndef BIT
 #define BIT(x) (1U << (x))
 #endif
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index c40e955..ce09970 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -1,14 +1,3 @@
-all: libcrypto.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcrypto.a
-
-install:
-	@echo Nothing to be made.
-
-
-include ../lib.rules
-
 CFLAGS += -DCONFIG_CRYPTO_INTERNAL
 CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
 CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
@@ -68,8 +57,4 @@
 LIB_OBJS += random.o
 endif
 
-
-libcrypto.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index de2b077..7d2ebd6 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -918,4 +918,16 @@
 void crypto_ecdh_deinit(struct crypto_ecdh *ecdh);
 size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh);
 
+struct crypto_ec_key;
+
+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len);
+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len);
+void crypto_ec_key_deinit(struct crypto_ec_key *key);
+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key);
+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
+				   size_t len);
+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
+				   size_t len, const u8 *sig, size_t sig_len);
+int crypto_ec_key_group(struct crypto_ec_key *key);
+
 #endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index 1cc73d8..fafb688 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -744,6 +744,155 @@
 }
 
 
+static int test_aes_ctr(void)
+{
+	int res = 0;
+
+#if defined(CONFIG_MESH) || defined(CONFIG_PSK)
+	/* CTR-AES*.Encrypt test vectors from NIST SP 800-38a */
+	const u8 key128[] = {
+		0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+	};
+	const u8 counter128[] = {
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	const u8 plain128[] = {
+		0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+		0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+		0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+		0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+	};
+	const u8 cipher128[] = {
+		0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+		0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+		0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+		0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+		0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+		0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+		0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+		0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+	};
+	const u8 key192[] = {
+		0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52,
+		0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
+		0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b
+	};
+	const u8 counter192[] = {
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	const u8 plain192[] = {
+		0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+		0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+		0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+		0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+	};
+	const u8 cipher192[] = {
+		0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2,
+		0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b,
+		0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef,
+		0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94,
+		0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70,
+		0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7,
+		0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58,
+		0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50
+	};
+	const u8 key256[] = {
+		0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
+		0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+		0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
+		0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
+	};
+	const u8 counter256[] = {
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	const u8 plain256[] = {
+		0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+		0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+		0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+		0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+	};
+	const u8 cipher256[] = {
+		0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5,
+		0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,
+		0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a,
+		0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5,
+		0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c,
+		0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d,
+		0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6,
+		0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6
+	};
+	size_t len;
+	u8 *tmp;
+
+	wpa_printf(MSG_DEBUG, "CTR-AES128.Encrypt");
+	len = sizeof(plain128);
+	tmp = os_malloc(len);
+	if (!tmp)
+		return -1;
+	os_memcpy(tmp, plain128, len);
+	if (aes_ctr_encrypt(key128, sizeof(key128), counter128, tmp, len) < 0) {
+		wpa_printf(MSG_ERROR, "aes_ctr_encrypt() failed");
+		res = -1;
+	} else if (os_memcmp(tmp, cipher128, len) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "CTR-AES128.Encrypt test vector did not match");
+		res = -1;
+	}
+	os_free(tmp);
+
+	wpa_printf(MSG_DEBUG, "CTR-AES192.Encrypt");
+	len = sizeof(plain192);
+	tmp = os_malloc(len);
+	if (!tmp)
+		return -1;
+	os_memcpy(tmp, plain192, len);
+	if (aes_ctr_encrypt(key192, sizeof(key192), counter192, tmp, len) < 0) {
+		wpa_printf(MSG_ERROR, "aes_ctr_encrypt() failed");
+		res = -1;
+	} else if (os_memcmp(tmp, cipher192, len) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "CTR-AES192.Encrypt test vector did not match");
+		res = -1;
+	}
+	os_free(tmp);
+
+	wpa_printf(MSG_DEBUG, "CTR-AES256.Encrypt");
+	len = sizeof(plain256);
+	tmp = os_malloc(len);
+	if (!tmp)
+		return -1;
+	os_memcpy(tmp, plain256, len);
+	if (aes_ctr_encrypt(key256, sizeof(key256), counter256, tmp, len) < 0) {
+		wpa_printf(MSG_ERROR, "aes_ctr_encrypt() failed");
+		res = -1;
+	} else if (os_memcmp(tmp, cipher256, len) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "CTR-AES256.Encrypt test vector did not match");
+		res = -1;
+	}
+	os_free(tmp);
+#endif
+
+	return res;
+}
+
+
 static int test_md5(void)
 {
 #ifndef CONFIG_FIPS
@@ -2154,6 +2303,7 @@
 	    test_cbc() ||
 	    test_ecb() ||
 	    test_key_wrap() ||
+	    test_aes_ctr() ||
 	    test_md5() ||
 	    test_sha1() ||
 	    test_sha256() ||
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 47b6ebb..72f93c1 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -21,6 +21,7 @@
 #endif /* CONFIG_OPENSSL_CMAC */
 #ifdef CONFIG_ECC
 #include <openssl/ec.h>
+#include <openssl/x509.h>
 #endif /* CONFIG_ECC */
 
 #include "common.h"
@@ -79,6 +80,14 @@
 	bin_clear_free(ctx, sizeof(*ctx));
 }
 
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+	if (pkey->type != EVP_PKEY_EC)
+		return NULL;
+	return pkey->pkey.ec;
+}
+
 #endif /* OpenSSL version < 1.1.0 */
 
 static BIGNUM * get_group5_prime(void)
@@ -2174,4 +2183,164 @@
 	return crypto_ec_prime_len(ecdh->ec);
 }
 
+
+struct crypto_ec_key {
+	EVP_PKEY *pkey;
+	EC_KEY *eckey;
+};
+
+
+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
+{
+	struct crypto_ec_key *key;
+
+	key = os_zalloc(sizeof(*key));
+	if (!key)
+		return NULL;
+
+	key->eckey = d2i_ECPrivateKey(NULL, &der, der_len);
+	if (!key->eckey) {
+		wpa_printf(MSG_INFO, "OpenSSL: d2i_ECPrivateKey() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	EC_KEY_set_conv_form(key->eckey, POINT_CONVERSION_COMPRESSED);
+
+	key->pkey = EVP_PKEY_new();
+	if (!key->pkey || EVP_PKEY_assign_EC_KEY(key->pkey, key->eckey) != 1) {
+		EC_KEY_free(key->eckey);
+		key->eckey = NULL;
+		goto fail;
+	}
+
+	return key;
+fail:
+	crypto_ec_key_deinit(key);
+	return NULL;
+}
+
+
+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
+{
+	struct crypto_ec_key *key;
+
+	key = os_zalloc(sizeof(*key));
+	if (!key)
+		return NULL;
+
+	key->pkey = d2i_PUBKEY(NULL, &der, der_len);
+	if (!key->pkey) {
+		wpa_printf(MSG_INFO, "OpenSSL: d2i_PUBKEY() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+
+	key->eckey = EVP_PKEY_get0_EC_KEY(key->pkey);
+	if (!key->eckey)
+		goto fail;
+	return key;
+fail:
+	crypto_ec_key_deinit(key);
+	return NULL;
+}
+
+
+void crypto_ec_key_deinit(struct crypto_ec_key *key)
+{
+	if (key) {
+		EVP_PKEY_free(key->pkey);
+		os_free(key);
+	}
+}
+
+
+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
+{
+	unsigned char *der = NULL;
+	int der_len;
+	struct wpabuf *buf;
+
+	der_len = i2d_PUBKEY(key->pkey, &der);
+	if (der_len <= 0) {
+		wpa_printf(MSG_INFO, "OpenSSL: i2d_PUBKEY() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		return NULL;
+	}
+
+	buf = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+	return buf;
+}
+
+
+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
+				   size_t len)
+{
+	EVP_PKEY_CTX *pkctx;
+	struct wpabuf *sig_der;
+	size_t sig_len;
+
+	sig_len = EVP_PKEY_size(key->pkey);
+	sig_der = wpabuf_alloc(sig_len);
+	if (!sig_der)
+		return NULL;
+
+	pkctx = EVP_PKEY_CTX_new(key->pkey, NULL);
+	if (!pkctx ||
+	    EVP_PKEY_sign_init(pkctx) <= 0 ||
+	    EVP_PKEY_sign(pkctx, wpabuf_put(sig_der, 0), &sig_len,
+			  data, len) <= 0) {
+		wpabuf_free(sig_der);
+		sig_der = NULL;
+	} else {
+		wpabuf_put(sig_der, sig_len);
+	}
+
+	EVP_PKEY_CTX_free(pkctx);
+	return sig_der;
+}
+
+
+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
+				   size_t len, const u8 *sig, size_t sig_len)
+{
+	EVP_PKEY_CTX *pkctx;
+	int ret;
+
+	pkctx = EVP_PKEY_CTX_new(key->pkey, NULL);
+	if (!pkctx || EVP_PKEY_verify_init(pkctx) <= 0) {
+		EVP_PKEY_CTX_free(pkctx);
+		return -1;
+	}
+
+	ret = EVP_PKEY_verify(pkctx, sig, sig_len, data, len);
+	EVP_PKEY_CTX_free(pkctx);
+	if (ret == 1)
+		return 1; /* signature ok */
+	if (ret == 0)
+		return 0; /* incorrect signature */
+	return -1;
+}
+
+
+int crypto_ec_key_group(struct crypto_ec_key *key)
+{
+	const EC_GROUP *group;
+	int nid;
+
+	group = EC_KEY_get0_group(key->eckey);
+	if (!group)
+		return -1;
+	nid = EC_GROUP_get_curve_name(group);
+	switch (nid) {
+	case NID_X9_62_prime256v1:
+		return 19;
+	case NID_secp384r1:
+		return 20;
+	case NID_secp521r1:
+		return 21;
+	}
+	return -1;
+}
+
 #endif /* CONFIG_ECC */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index c8b1a82..09fb73b 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -670,4 +670,18 @@
  */
 u16 tls_connection_get_cipher_suite(struct tls_connection *conn);
 
+/**
+ * tls_connection_get_peer_subject - Get peer subject
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Peer subject or %NULL if not authenticated or not available
+ */
+const char * tls_connection_get_peer_subject(struct tls_connection *conn);
+
+/**
+ * tls_connection_get_own_cert_used - Was own certificate used
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: true if own certificate was used during authentication
+ */
+bool tls_connection_get_own_cert_used(struct tls_connection *conn);
+
 #endif /* TLS_H */
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 7ee371a..a789455 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -279,6 +279,7 @@
 	X509 *peer_cert;
 	X509 *peer_issuer;
 	X509 *peer_issuer_issuer;
+	char *peer_subject; /* peer subject info for authenticated peer */
 
 	unsigned char client_random[SSL3_RANDOM_SIZE];
 	unsigned char server_random[SSL3_RANDOM_SIZE];
@@ -1643,6 +1644,7 @@
 	os_free(conn->domain_match);
 	os_free(conn->check_cert_subject);
 	os_free(conn->session_ticket);
+	os_free(conn->peer_subject);
 	os_free(conn);
 }
 
@@ -2597,6 +2599,11 @@
 		context->event_cb(context->cb_ctx,
 				  TLS_CERT_CHAIN_SUCCESS, NULL);
 
+	if (depth == 0 && preverify_ok) {
+		os_free(conn->peer_subject);
+		conn->peer_subject = os_strdup(buf);
+	}
+
 	return preverify_ok;
 }
 
@@ -3006,16 +3013,12 @@
 
 		/* Explicit request to enable TLS versions even if needing to
 		 * override systemwide policies. */
-		if (flags & TLS_CONN_ENABLE_TLSv1_0) {
+		if (flags & TLS_CONN_ENABLE_TLSv1_0)
 			version = TLS1_VERSION;
-		} else if (flags & TLS_CONN_ENABLE_TLSv1_1) {
-			if (!(flags & TLS_CONN_DISABLE_TLSv1_0))
-				version = TLS1_1_VERSION;
-		} else if (flags & TLS_CONN_ENABLE_TLSv1_2) {
-			if (!(flags & (TLS_CONN_DISABLE_TLSv1_0 |
-				       TLS_CONN_DISABLE_TLSv1_1)))
-				version = TLS1_2_VERSION;
-		}
+		else if (flags & TLS_CONN_ENABLE_TLSv1_1)
+			version = TLS1_1_VERSION;
+		else if (flags & TLS_CONN_ENABLE_TLSv1_2)
+			version = TLS1_2_VERSION;
 		if (!version) {
 			wpa_printf(MSG_DEBUG,
 				   "OpenSSL: Invalid TLS version configuration");
@@ -3029,6 +3032,18 @@
 		}
 	}
 #endif /* >= 1.1.0 */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+	!defined(LIBRESSL_VERSION_NUMBER) && \
+	!defined(OPENSSL_IS_BORINGSSL)
+	if ((flags & (TLS_CONN_ENABLE_TLSv1_0 | TLS_CONN_ENABLE_TLSv1_1)) &&
+	    SSL_get_security_level(ssl) >= 2) {
+		/*
+		 * Need to drop to security level 1 to allow TLS versions older
+		 * than 1.2 to be used when explicitly enabled in configuration.
+		 */
+		SSL_set_security_level(conn->ssl, 1);
+	}
+#endif
 
 #ifdef CONFIG_SUITEB
 #ifdef OPENSSL_IS_BORINGSSL
@@ -3198,7 +3213,11 @@
 	if (conn == NULL)
 		return -1;
 
-	if (verify_peer) {
+	if (verify_peer == 2) {
+		conn->ca_cert_verify = 1;
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+			       SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
+	} else if (verify_peer) {
 		conn->ca_cert_verify = 1;
 		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
 			       SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
@@ -3259,8 +3278,36 @@
 			   "OK");
 		return 0;
 	} else if (client_cert_blob) {
+#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20901000L
 		tls_show_errors(MSG_DEBUG, __func__,
 				"SSL_use_certificate_ASN1 failed");
+#else
+		BIO *bio;
+		X509 *x509;
+
+		tls_show_errors(MSG_DEBUG, __func__,
+				"SSL_use_certificate_ASN1 failed");
+		bio = BIO_new(BIO_s_mem());
+		if (!bio)
+			return -1;
+		BIO_write(bio, client_cert_blob, client_cert_blob_len);
+		x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+		if (!x509 || SSL_use_certificate(conn->ssl, x509) != 1) {
+			X509_free(x509);
+			BIO_free(bio);
+			return -1;
+		}
+		X509_free(x509);
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Found PEM encoded certificate from blob");
+		while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL))) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Added an additional certificate into the chain");
+			SSL_add0_chain_cert(conn->ssl, x509);
+		}
+		BIO_free(bio);
+		return 0;
+#endif
 	}
 
 	if (client_cert == NULL)
@@ -3767,6 +3814,17 @@
 			break;
 		}
 
+#ifndef OPENSSL_NO_EC
+		if (SSL_use_PrivateKey_ASN1(EVP_PKEY_EC, conn->ssl,
+					    (u8 *) private_key_blob,
+					    private_key_blob_len) == 1) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: SSL_use_PrivateKey_ASN1(EVP_PKEY_EC) --> OK");
+			ok = 1;
+			break;
+		}
+#endif /* OPENSSL_NO_EC */
+
 		if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
 					       (u8 *) private_key_blob,
 					       private_key_blob_len) == 1) {
@@ -5667,3 +5725,19 @@
 	return SSL_CIPHER_get_id(cipher) & 0xFFFF;
 #endif
 }
+
+
+const char * tls_connection_get_peer_subject(struct tls_connection *conn)
+{
+	if (conn)
+		return conn->peer_subject;
+	return NULL;
+}
+
+
+bool tls_connection_get_own_cert_used(struct tls_connection *conn)
+{
+	if (conn)
+		return SSL_get_certificate(conn->ssl) != NULL;
+	return false;
+}
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index 11e6582..b8a7665 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -19,6 +19,7 @@
 #include <wolfssl/ssl.h>
 #include <wolfssl/error-ssl.h>
 #include <wolfssl/wolfcrypt/asn.h>
+#include <wolfssl/openssl/x509v3.h>
 
 #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
 #define HAVE_AESGCM
@@ -576,7 +577,7 @@
 static int tls_match_alt_subject_component(WOLFSSL_X509 *cert, int type,
 					   const char *value, size_t len)
 {
-	WOLFSSL_ASN1_OBJECT *gen;
+	WOLFSSL_GENERAL_NAME *gen;
 	void *ext;
 	int found = 0;
 	int i;
@@ -585,14 +586,15 @@
 
 	for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
 		gen = wolfSSL_sk_value(ext, i);
-		if (gen->type != type)
+		if (!gen || gen->type != type)
 			continue;
-		if (os_strlen((char *) gen->obj) == len &&
-		    os_memcmp(value, gen->obj, len) == 0)
+		if ((size_t) wolfSSL_ASN1_STRING_length(gen->d.ia5) == len &&
+		    os_memcmp(value, wolfSSL_ASN1_STRING_data(gen->d.ia5),
+			      len) == 0)
 			found++;
 	}
 
-	wolfSSL_sk_ASN1_OBJECT_free(ext);
+	wolfSSL_sk_GENERAL_NAME_free(ext);
 
 	return found;
 }
@@ -676,7 +678,7 @@
 static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match,
 				   size_t match_len, int full)
 {
-	WOLFSSL_ASN1_OBJECT *gen;
+	WOLFSSL_GENERAL_NAME *gen;
 	void *ext;
 	int i;
 	int j;
@@ -690,21 +692,23 @@
 
 	for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) {
 		gen = wolfSSL_sk_value(ext, j);
-		if (gen->type != ASN_DNS_TYPE)
+		if (!gen || gen->type != ASN_DNS_TYPE)
 			continue;
 		dns_name++;
 		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
-				  gen->obj, os_strlen((char *)gen->obj));
-		if (domain_suffix_match((const char *) gen->obj,
-					os_strlen((char *) gen->obj), match,
-					match_len, full) == 1) {
+				  wolfSSL_ASN1_STRING_data(gen->d.ia5),
+				  wolfSSL_ASN1_STRING_length(gen->d.ia5));
+		if (domain_suffix_match(
+			    (const char *) wolfSSL_ASN1_STRING_data(gen->d.ia5),
+			    wolfSSL_ASN1_STRING_length(gen->d.ia5), match,
+			    match_len, full) == 1) {
 			wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
 				   full ? "Match" : "Suffix match");
 			wolfSSL_sk_ASN1_OBJECT_free(ext);
 			return 1;
 		}
 	}
-	wolfSSL_sk_ASN1_OBJECT_free(ext);
+	wolfSSL_sk_GENERAL_NAME_free(ext);
 
 	if (dns_name) {
 		wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
@@ -858,7 +862,7 @@
 	struct tls_context *context = conn->context;
 	char *alt_subject[TLS_MAX_ALT_SUBJECT];
 	int alt, num_alt_subject = 0;
-	WOLFSSL_ASN1_OBJECT *gen;
+	WOLFSSL_GENERAL_NAME *gen;
 	void *ext;
 	int i;
 #ifdef CONFIG_SHA256
@@ -899,12 +903,14 @@
 		if (num_alt_subject == TLS_MAX_ALT_SUBJECT)
 			break;
 		gen = wolfSSL_sk_value((void *) ext, i);
-		if (gen->type != GEN_EMAIL &&
-		    gen->type != GEN_DNS &&
-		    gen->type != GEN_URI)
+		if (!gen ||
+		    (gen->type != GEN_EMAIL &&
+		     gen->type != GEN_DNS &&
+		     gen->type != GEN_URI))
 			continue;
 
-		pos = os_malloc(10 + os_strlen((char *) gen->obj) + 1);
+		pos = os_malloc(10 + wolfSSL_ASN1_STRING_length(gen->d.ia5) +
+				1);
 		if (!pos)
 			break;
 		alt_subject[num_alt_subject++] = pos;
@@ -924,11 +930,12 @@
 			break;
 		}
 
-		os_memcpy(pos, gen->obj, os_strlen((char *)gen->obj));
-		pos += os_strlen((char *)gen->obj);
+		os_memcpy(pos, wolfSSL_ASN1_STRING_data(gen->d.ia5),
+			  wolfSSL_ASN1_STRING_length(gen->d.ia5));
+		pos += wolfSSL_ASN1_STRING_length(gen->d.ia5);
 		*pos = '\0';
 	}
-	wolfSSL_sk_ASN1_OBJECT_free(ext);
+	wolfSSL_sk_GENERAL_NAME_free(ext);
 
 	for (alt = 0; alt < num_alt_subject; alt++)
 		ev.peer_cert.altsubject[alt] = alt_subject[alt];
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 350c1cb..e8defab 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -197,6 +197,7 @@
 	u8 mac_cap[HE_MAX_MAC_CAPAB_SIZE];
 	u8 mcs[HE_MAX_MCS_CAPAB_SIZE];
 	u8 ppet[HE_MAX_PPET_CAPAB_SIZE];
+	u16 he_6ghz_capa;
 };
 
 #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
@@ -1930,6 +1931,8 @@
 
 /** Driver supports a separate control port RX for EAPOL frames */
 #define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX	0x0000000000000001ULL
+/** Driver supports TX status reports for EAPOL frames through control port */
+#define WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS 0x0000000000000002ULL
 	u64 flags2;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -4384,13 +4387,13 @@
 	int (*ignore_assoc_disallow)(void *priv, int ignore_disallow);
 
 	/**
-	 * set_bssid_blacklist - Set blacklist of BSSIDs to the driver
+	 * set_bssid_tmp_disallow - Set disallowed BSSIDs to the driver
 	 * @priv: Private driver interface data
-	 * @num_bssid: Number of blacklist BSSIDs
-	 * @bssids: List of blacklisted BSSIDs
+	 * @num_bssid: Number of temporarily disallowed BSSIDs
+	 * @bssids: List of temporarily disallowed BSSIDs
 	 */
-	int (*set_bssid_blacklist)(void *priv, unsigned int num_bssid,
-				   const u8 *bssid);
+	int (*set_bssid_tmp_disallow)(void *priv, unsigned int num_bssid,
+				      const u8 *bssid);
 
 	/**
 	 * update_connect_params - Update the connection parameters
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 5adee13..a29d2c8 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -137,7 +137,9 @@
 	ireq->i_data = arg;
 
 	if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOCG80211, op=%u, "
+		int level = drv->if_removed ? MSG_DEBUG : MSG_ERROR;
+
+		wpa_printf(level, "ioctl[SIOCG80211, op=%u, "
 			   "arg_len=%u]: %s", op, arg_len, strerror(errno));
 		return -1;
 	}
@@ -1468,6 +1470,9 @@
 	drv->global = priv;
 	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
 
+	/* Set the interface as removed until proven to work. */
+	drv->if_removed = 1;
+
 	if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
 		wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
 			__func__, strerror(errno));
@@ -1495,6 +1500,9 @@
 	if (bsd_get_iface_flags(drv) < 0)
 		goto fail;
 
+	/* Proven to work, lets go! */
+	drv->if_removed = 0;
+
 	drv->opmode = get80211opmode(drv);
 	dl_list_add(&drv->global->ifaces, &drv->list);
 
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 23a6a42..a7ebe95 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -328,6 +328,7 @@
 #define DF2S(x) case WPA_DRIVER_FLAGS2_ ## x: return #x
 	switch (flag2) {
 	DF2S(CONTROL_PORT_RX);
+	DF2S(CONTROL_PORT_TX_STATUS);
 	}
 	return "UNKNOWN";
 #undef DF2S
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
index 36a0757..3dba13c 100644
--- a/src/drivers/driver_macsec_linux.c
+++ b/src/drivers/driver_macsec_linux.c
@@ -712,6 +712,9 @@
 	if (!msg)
 		return ret;
 
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+		goto nla_put_failure;
+
 	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
 	if (!nest)
 		goto nla_put_failure;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 5a760ff..4295f0c 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -280,6 +280,43 @@
 	return NL_STOP;
 }
 
+
+struct nl80211_ack_ext_arg {
+	int *err;
+	void *ext_data;
+};
+
+
+static int ack_handler_cookie(struct nl_msg *msg, void *arg)
+{
+	struct nl80211_ack_ext_arg *ext_arg = arg;
+	struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
+	u64 *cookie = ext_arg->ext_data;
+	struct nlattr *attrs;
+	size_t ack_len, attr_len;
+
+	*ext_arg->err = 0;
+	ack_len = sizeof(struct nlmsghdr) + sizeof(int) +
+		sizeof(struct nlmsghdr);
+	attrs = (struct nlattr *)
+		((u8 *) nlmsg_data(nlmsg_hdr(msg)) + sizeof(struct nlmsghdr) +
+		 sizeof(int));
+	if (nlmsg_hdr(msg)->nlmsg_len <= ack_len)
+		return NL_STOP;
+
+	attr_len = nlmsg_hdr(msg)->nlmsg_len - ack_len;
+
+	if(!(nlmsg_hdr(msg)->nlmsg_flags & NLM_F_ACK_TLVS))
+		return NL_STOP;
+
+	nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, attr_len, NULL);
+	if (tb[NLMSGERR_ATTR_COOKIE])
+		*cookie = nla_get_u64(tb[NLMSGERR_ATTR_COOKIE]);
+
+	return NL_STOP;
+}
+
+
 static int finish_handler(struct nl_msg *msg, void *arg)
 {
 	int *ret = arg;
@@ -354,7 +391,9 @@
 static int send_and_recv(struct nl80211_global *global,
 			 struct nl_sock *nl_handle, struct nl_msg *msg,
 			 int (*valid_handler)(struct nl_msg *, void *),
-			 void *valid_data)
+			 void *valid_data,
+			 int (*ack_handler_custom)(struct nl_msg *, void *),
+			 void *ack_data)
 {
 	struct nl_cb *cb;
 	int err = -ENOMEM, opt;
@@ -392,7 +431,15 @@
 
 	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
 	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
-	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+	if (ack_handler_custom) {
+		struct nl80211_ack_ext_arg *ext_arg = ack_data;
+
+		ext_arg->err = &err;
+		nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM,
+			  ack_handler_custom, ack_data);
+	} else {
+		nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+	}
 
 	if (valid_handler)
 		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
@@ -432,10 +479,13 @@
 int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
 		       struct nl_msg *msg,
 		       int (*valid_handler)(struct nl_msg *, void *),
-		       void *valid_data)
+		       void *valid_data,
+		       int (*ack_handler_custom)(struct nl_msg *, void *),
+		       void *ack_data)
 {
 	return send_and_recv(drv->global, drv->global->nl, msg,
-			     valid_handler, valid_data);
+			     valid_handler, valid_data,
+			     ack_handler_custom, ack_data);
 }
 
 
@@ -449,7 +499,10 @@
 				    struct nl_sock *handle, int set_owner,
 				    int (*valid_handler)(struct nl_msg *,
 							 void *),
-				    void *valid_data)
+				    void *valid_data,
+				    int (*ack_handler_custom)(struct nl_msg *,
+							      void *),
+				    void *ack_data)
 {
 	/* Control port over nl80211 needs the flags and attributes below.
 	 *
@@ -471,7 +524,8 @@
 		return -1;
 
 	return send_and_recv(drv->global, handle ? handle : drv->global->nl,
-			     msg, valid_handler, valid_data);
+			     msg, valid_handler, valid_data,
+			     ack_handler_custom, ack_data);
 }
 
 
@@ -539,7 +593,8 @@
 		return -1;
 	}
 
-	ret = send_and_recv(global, global->nl, msg, family_handler, &res);
+	ret = send_and_recv(global, global->nl, msg, family_handler, &res,
+			    NULL, NULL);
 	if (ret == 0)
 		ret = res.id;
 	return ret;
@@ -656,7 +711,8 @@
 	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
 		return -1;
 
-	if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
+	if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+			       NULL, NULL) == 0)
 		return data.wiphy_idx;
 	return -1;
 }
@@ -673,7 +729,8 @@
 	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
 		return NL80211_IFTYPE_UNSPECIFIED;
 
-	if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
+	if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+			       NULL, NULL) == 0)
 		return data.nlmode;
 	return NL80211_IFTYPE_UNSPECIFIED;
 }
@@ -689,7 +746,8 @@
 	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
 		return -1;
 
-	return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data);
+	return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+				  NULL, NULL);
 }
 
 
@@ -709,7 +767,8 @@
 		return -1;
 	}
 
-	ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL);
+	ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL,
+			    NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
 			   "failed: ret=%d (%s)",
@@ -1409,7 +1468,7 @@
 	os_memset(&arg, 0, sizeof(arg));
 	arg.drv = drv;
 	ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
-				 &arg);
+				 &arg, NULL, NULL);
 	if (ret == -EAGAIN) {
 		count++;
 		if (count >= 10) {
@@ -1443,7 +1502,7 @@
 	os_memset(&arg, 0, sizeof(arg));
 	arg.drv = drv;
 	ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
-				 &arg);
+				 &arg, NULL, NULL);
 	if (ret == -EAGAIN) {
 		count++;
 		if (count >= 10) {
@@ -1546,7 +1605,7 @@
 		return -ENOBUFS;
 	}
 
-	return send_and_recv_msgs(drv, msg, get_link_signal, sig);
+	return send_and_recv_msgs(drv, msg, get_link_signal, sig, NULL, NULL);
 }
 
 
@@ -1603,7 +1662,8 @@
 	sig_change->frequency = drv->assoc_freq;
 
 	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
-	return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
+	return send_and_recv_msgs(drv, msg, get_link_noise, sig_change,
+				  NULL, NULL);
 }
 
 
@@ -1667,7 +1727,7 @@
 	struct nl_msg *msg;
 
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
-	return send_and_recv_msgs(drv, msg, get_channel_info, ci);
+	return send_and_recv_msgs(drv, msg, get_channel_info, ci, NULL, NULL);
 }
 
 
@@ -1716,7 +1776,7 @@
 		nlmsg_free(msg);
 		return -EINVAL;
 	}
-	if (send_and_recv_msgs(drv, msg, NULL, NULL))
+	if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL))
 		return -EINVAL;
 	return 0;
 }
@@ -1752,7 +1812,8 @@
 
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
 	alpha2[0] = '\0';
-	ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2);
+	ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2,
+				 NULL, NULL);
 	if (!alpha2[0])
 		ret = -1;
 
@@ -2117,6 +2178,11 @@
 	if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
 		goto failed;
 
+	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS) {
+		drv->control_port_ap = 1;
+		goto skip_wifi_status;
+	}
+
 	drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
 	if (drv->eapol_tx_sock < 0)
 		goto failed;
@@ -2127,17 +2193,20 @@
 		if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS,
 			       &enabled, sizeof(enabled)) < 0) {
 			wpa_printf(MSG_DEBUG,
-				"nl80211: wifi status sockopt failed\n");
+				   "nl80211: wifi status sockopt failed: %s",
+				   strerror(errno));
 			drv->data_tx_status = 0;
 			if (!drv->use_monitor)
 				drv->capa.flags &=
 					~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
 		} else {
-			eloop_register_read_sock(drv->eapol_tx_sock,
+			eloop_register_read_sock(
+				drv->eapol_tx_sock,
 				wpa_driver_nl80211_handle_eapol_tx_status,
 				drv, NULL);
 		}
 	}
+skip_wifi_status:
 
 	if (drv->global) {
 		nl80211_check_global(drv->global);
@@ -2193,7 +2262,8 @@
 		return -1;
 	}
 
-	ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL);
+	ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL,
+			    NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
 			   "failed (type=%u): ret=%d (%s)",
@@ -2378,7 +2448,8 @@
 		ret = -1;
 
 	/* Radio Measurement - Radio Measurement Request */
-	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x00", 2) < 0)
+	if  (!drv->no_rrm &&
+	     nl80211_register_action_frame(bss, (u8 *) "\x05\x00", 2) < 0)
 		ret = -1;
 
 	/* Radio Measurement - Link Measurement Request */
@@ -2386,6 +2457,10 @@
 	    (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0))
 		ret = -1;
 
+	/* Robust AV MSCS Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x13\x05", 2) < 0)
+		ret = -1;
+
 	nl80211_mgmt_handle_register_eloop(bss);
 
 	return ret;
@@ -2432,7 +2507,8 @@
 	int ret;
 
 	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
-	ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL);
+	ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL,
+			    NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
 			   "failed: ret=%d (%s)",
@@ -2586,7 +2662,7 @@
 	int ret;
 
 	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE);
-	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
 
 	wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
 		   bss->ifname, (long long unsigned int) bss->wdev_id,
@@ -2601,7 +2677,7 @@
 
 	msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE :
 			      NL80211_CMD_STOP_P2P_DEVICE);
-	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
 
 	wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s",
 		   start ? "Start" : "Stop",
@@ -2672,7 +2748,8 @@
 	}
 	nla_nest_end(msg, params);
 
-	ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv);
+	ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv,
+				 NULL, NULL);
 	wpa_printf(MSG_DEBUG,
 		   "nl80211: QCA vendor test command returned %d (%s)",
 		   ret, strerror(-ret));
@@ -2797,7 +2874,7 @@
 		   drv->ifindex);
 	nl80211_put_wiphy_data_ap(bss);
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 }
 
 
@@ -3121,7 +3198,7 @@
 			" %d VHT: %d BW: %d\n",
 			params->hw_mode, params->ht_enabled, params->ht40_enabled,
 			params->vht_enabled, params->ch_width);
-		ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+		ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 		if (ret) {
 			wpa_printf(MSG_ERROR, "nl80211:	Failed to invoke vendor"
 				" driver ACS function: %s\n",
@@ -3151,7 +3228,7 @@
 		nlmsg_free(msg);
 		return -1;
 	}
-	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Key management set key failed: ret=%d (%s)",
@@ -3191,7 +3268,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)",
 			   ret, strerror(-ret));
@@ -3357,7 +3434,8 @@
 			goto fail;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL,
+				 NULL, NULL);
 	if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
 		ret = 0;
 	if (ret)
@@ -3419,7 +3497,7 @@
 			goto fail;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret)
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: set_key default failed; err=%d %s",
@@ -3543,9 +3621,10 @@
 	}
 
 	if (nl_connect)
-		ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL);
+		ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL,
+				    NULL, NULL);
 	else
-		ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+		ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_dbg(drv->ctx, MSG_DEBUG,
 			"nl80211: MLME command failed: reason=%u ret=%d (%s)",
@@ -3793,7 +3872,7 @@
 			goto fail;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -4071,7 +4150,7 @@
 		return -ENOBUFS;
 	}
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 }
 
 
@@ -4115,7 +4194,7 @@
 	}
 	nlmsg_free(acl);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)",
 			   ret, strerror(-ret));
@@ -4167,7 +4246,7 @@
 		return ret;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_ERROR,
 			   "nl80211: Mesh config set failed: %d (%s)",
@@ -4292,7 +4371,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 
 	switch (ret) {
 	case 0:
@@ -4581,7 +4660,7 @@
 #endif /* CONFIG_IEEE80211AX */
 
 	ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 1,
-				       NULL, NULL);
+				       NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
 			   ret, strerror(-ret));
@@ -4750,7 +4829,7 @@
 		return -1;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret == 0) {
 		bss->freq = freq->freq;
 		return 0;
@@ -5028,7 +5107,7 @@
 		nla_nest_end(msg, wme);
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret)
 		wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
@@ -5100,7 +5179,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR
 		   " --> %d (%s)",
 		   bss->ifname, MAC2STR(addr), ret, strerror(-ret));
@@ -5131,7 +5210,7 @@
 	}
 
 	msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
-	if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+	if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL) == 0)
 		return;
 	wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
 }
@@ -5211,7 +5290,7 @@
 	if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
 		goto fail;
 
-	ret = send_and_recv_msgs(drv, msg, handler, arg);
+	ret = send_and_recv_msgs(drv, msg, handler, arg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 	fail:
@@ -5366,8 +5445,10 @@
 				   u16 proto, const u8 *buf, size_t len,
 				   int no_encrypt)
 {
+	struct nl80211_ack_ext_arg ext_arg;
 	struct i802_bss *bss = priv;
 	struct nl_msg *msg;
+	u64 cookie = 0;
 	int ret;
 
 	wpa_printf(MSG_DEBUG,
@@ -5386,11 +5467,22 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
-	if (ret)
+	os_memset(&ext_arg, 0, sizeof(struct nl80211_ack_ext_arg));
+	ext_arg.ext_data = &cookie;
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL,
+				 ack_handler_cookie, &ext_arg);
+	if (ret) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: tx_control_port failed: ret=%d (%s)",
 			   ret, strerror(-ret));
+	} else {
+		struct wpa_driver_nl80211_data *drv = bss->drv;
+
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: tx_control_port cookie=0x%llx",
+			   (long long unsigned int) cookie);
+		drv->eapol_tx_cookie = cookie;
+	}
 
 	return ret;
 }
@@ -5542,7 +5634,7 @@
 	if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
 		goto fail;
 
-	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
 fail:
 	nlmsg_free(msg);
 	return -ENOBUFS;
@@ -5564,7 +5656,7 @@
 	    nla_put_u16(msg, NL80211_ATTR_AIRTIME_WEIGHT, weight))
 		goto fail;
 
-	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
 fail:
 	nlmsg_free(msg);
 	return -ENOBUFS;
@@ -5610,7 +5702,7 @@
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
 	ret = send_and_recv_msgs_owner(drv, msg,
 				       get_connect_handle(drv->first_bss), 1,
-				       NULL, NULL);
+				       NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
@@ -5744,7 +5836,7 @@
 
 	ret = send_and_recv_msgs_owner(drv, msg,
 				       get_connect_handle(drv->first_bss), 1,
-				       NULL, NULL);
+				       NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
@@ -6146,7 +6238,7 @@
 		goto fail;
 
 	ret = send_and_recv_msgs_owner(drv, msg, nl_connect, 1, NULL,
-				       (void *) -1);
+				       (void *) -1, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
@@ -6262,7 +6354,7 @@
 
 	ret = send_and_recv_msgs_owner(drv, msg,
 				       get_connect_handle(drv->first_bss), 1,
-				       NULL, NULL);
+				       NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -6293,7 +6385,7 @@
 	if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
 		goto fail;
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (!ret)
 		return 0;
@@ -6554,7 +6646,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (!ret)
 		return 0;
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
@@ -6619,7 +6711,7 @@
 
 	memset(seq, 0, 6);
 
-	return send_and_recv_msgs(drv, msg, get_key_handler, seq);
+	return send_and_recv_msgs(drv, msg, get_key_handler, seq, NULL, NULL);
 }
 
 
@@ -6642,7 +6734,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (!ret)
 		return 0;
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
@@ -6670,7 +6762,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (!ret)
 		return 0;
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
@@ -6692,7 +6784,7 @@
 	 * XXX: FIX! this needs to flush all VLANs too
 	 */
 	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
-	res = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+	res = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
 	if (res) {
 		wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
 			   "(%s)", res, strerror(-res));
@@ -6901,7 +6993,8 @@
 		return -ENOBUFS;
 	}
 
-	return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data);
+	return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data,
+				  NULL, NULL);
 }
 
 
@@ -6958,7 +7051,7 @@
 
 	nla_nest_end(msg, txq);
 
-	res = send_and_recv_msgs(drv, msg, NULL, NULL);
+	res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	wpa_printf(MSG_DEBUG,
 		   "nl80211: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d",
 		   queue, aifs, cw_min, cw_max, burst_time, res);
@@ -6984,14 +7077,14 @@
 		   MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id);
 	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
-	    ((drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
+	    (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
 	     nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id)) ||
 	    nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret < 0) {
 		wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
 			   MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
@@ -7596,8 +7689,10 @@
 
 		drv->global->if_add_wdevid = p2pdev_info.wdev_id;
 		drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set;
-		if (!is_zero_ether_addr(p2pdev_info.macaddr))
+		if (!is_zero_ether_addr(p2pdev_info.macaddr)) {
 			os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN);
+			os_memcpy(drv->global->p2p_perm_addr, p2pdev_info.macaddr, ETH_ALEN);
+		}
 		wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created",
 			   ifname,
 			   (long long unsigned int) p2pdev_info.wdev_id);
@@ -7838,7 +7933,7 @@
 		goto fail;
 
 	cookie = 0;
-	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
@@ -7945,7 +8040,7 @@
 		return;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret)
 		wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
@@ -7993,7 +8088,7 @@
 	}
 
 	cookie = 0;
-	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
 	if (ret == 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
 			   "0x%llx for freq=%u MHz duration=%u",
@@ -8033,7 +8128,7 @@
 		return -1;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret == 0)
 		return 0;
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
@@ -8130,7 +8225,7 @@
 
 	nla_nest_end(msg, bands);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
@@ -8227,7 +8322,7 @@
 	}
 	nla_nest_end(msg, cqm);
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 }
 
 
@@ -8265,7 +8360,7 @@
 	struct nl_msg *msg;
 
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
-	return send_and_recv_msgs(drv, msg, get_channel_width, sig);
+	return send_and_recv_msgs(drv, msg, get_channel_width, sig, NULL, NULL);
 }
 
 
@@ -8328,15 +8423,34 @@
 
 	if (os_strstr(param, "control_port=0")) {
 		drv->capa.flags &= ~WPA_DRIVER_FLAGS_CONTROL_PORT;
-		drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_CONTROL_PORT_RX;
+		drv->capa.flags2 &= ~(WPA_DRIVER_FLAGS2_CONTROL_PORT_RX |
+				      WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS);
+		drv->control_port_ap = 0;
 	}
 
 	if (os_strstr(param, "control_port_ap=1"))
 		drv->control_port_ap = 1;
 
+	if (os_strstr(param, "control_port_ap=0")) {
+		drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS;
+		drv->control_port_ap = 0;
+	}
+
 	if (os_strstr(param, "full_ap_client_state=0"))
 		drv->capa.flags &= ~WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
 
+	if (os_strstr(param, "no_rrm=1")) {
+		drv->no_rrm = 1;
+
+		if (!bss->in_deinit && !is_ap_interface(drv->nlmode) &&
+		    !is_mesh_interface(drv->nlmode)) {
+			nl80211_mgmt_unsubscribe(bss, "no_rrm=1");
+			if (nl80211_mgmt_subscribe_non_ap(bss) < 0)
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Failed to re-register Action frame processing - ignore for now");
+		}
+	}
+
 	return 0;
 }
 
@@ -8451,7 +8565,7 @@
 		return -ENOBUFS;
 	}
 
-	return send_and_recv_msgs(bss->drv, msg, NULL, (void *) -1);
+	return send_and_recv_msgs(bss->drv, msg, NULL, (void *) -1, NULL, NULL);
 }
 
 
@@ -8516,7 +8630,7 @@
 	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA);
 	if (!msg)
 		return -ENOBUFS;
-	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
 }
 
 
@@ -8681,7 +8795,7 @@
 	do {
 		wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data");
 		err = send_and_recv_msgs(drv, msg, survey_handler,
-					 survey_results);
+					 survey_results, NULL, NULL);
 	} while (err > 0);
 
 	if (err)
@@ -8721,7 +8835,7 @@
 
 	nla_nest_end(msg, replay_nested);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
 	if (ret == -EOPNOTSUPP) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Driver does not support rekey offload");
@@ -8788,7 +8902,7 @@
 		return;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
 	if (ret < 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
 			   MACSTR " failed: ret=%d (%s)",
@@ -8814,7 +8928,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
 	if (ret < 0) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Setting PS state %s failed: %d (%s)",
@@ -8874,7 +8988,7 @@
 		return -1;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret == 0)
 		return 0;
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
@@ -8921,7 +9035,7 @@
 	    nla_put(msg, NL80211_ATTR_IE, len, buf))
 		goto fail;
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 
 fail:
 	nlmsg_free(msg);
@@ -8971,7 +9085,7 @@
 		return -ENOBUFS;
 	}
 
-	res = send_and_recv_msgs(drv, msg, NULL, NULL);
+	res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR
 		   " --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res,
 		   strerror(-res));
@@ -9005,7 +9119,7 @@
 		return ret;
 	}
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 }
 
 
@@ -9031,7 +9145,7 @@
 		return -ENOBUFS;
 	}
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 }
 
 #endif /* CONFIG TDLS */
@@ -9174,7 +9288,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed "
 			   "err=%d (%s)", ret, strerror(-ret));
@@ -9202,7 +9316,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: update_dh_ie failed err=%d (%s)",
@@ -9407,7 +9521,7 @@
 	    nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG) &&
 	    nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx) == 0) {
 		if (send_and_recv_msgs(drv, msg, nl80211_get_country,
-				       alpha2) == 0 &&
+				       alpha2, NULL, NULL) == 0 &&
 		    alpha2[0]) {
 			res = os_snprintf(pos, end - pos, "country=%s\n",
 					  alpha2);
@@ -9558,7 +9672,7 @@
 		goto fail;
 
 	nla_nest_end(msg, beacon_csa);
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)",
 			   ret, strerror(-ret));
@@ -9599,7 +9713,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret)
 		wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)",
 			   ret, strerror(-ret));
@@ -9626,7 +9740,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret)
 		wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)",
 			   ret, strerror(-ret));
@@ -9711,7 +9825,8 @@
 		 * of send_and_recv_msgs(). */
 		ret = send_and_recv_msgs_owner(drv, msg,
 					       get_connect_handle(bss), 0,
-					       cmd_reply_handler, buf);
+					       cmd_reply_handler, buf,
+					       NULL, NULL);
 		if (ret)
 			wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
 				   ret);
@@ -9726,7 +9841,8 @@
 	     nla_put(msg, NL80211_ATTR_VENDOR_DATA, data_len, data)))
 		goto fail;
 
-	ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf);
+	ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf,
+				 NULL, NULL);
 	if (ret)
 		wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d",
 			   ret);
@@ -9755,7 +9871,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret)
 		wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
 
@@ -9790,7 +9906,8 @@
 
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_WOWLAN);
 
-	ret = send_and_recv_msgs(drv, msg, get_wowlan_handler, &wowlan_enabled);
+	ret = send_and_recv_msgs(drv, msg, get_wowlan_handler, &wowlan_enabled,
+				 NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status failed");
 		return 0;
@@ -9837,7 +9954,7 @@
 
 	nla_nest_end(msg, wowlan_triggers);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret)
 		wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed");
 
@@ -9876,7 +9993,7 @@
 	}
 	nla_nest_end(msg, params);
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 }
 
 
@@ -9904,15 +10021,15 @@
 	}
 	nla_nest_end(msg, params);
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 }
 
 
 /* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */
 #define WPA_SUPPLICANT_CLIENT_ID 1
 
-static int nl80211_set_bssid_blacklist(void *priv, unsigned int num_bssid,
-				       const u8 *bssid)
+static int nl80211_set_bssid_tmp_disallow(void *priv, unsigned int num_bssid,
+					  const u8 *bssid)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -9920,7 +10037,8 @@
 	struct nlattr *params, *nlbssids, *attr;
 	unsigned int i;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Set blacklist BSSID (num=%u)",
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set temporarily disallowed BSSIDs (num=%u)",
 		   num_bssid);
 
 	if (!drv->roam_vendor_cmd_avail)
@@ -9960,7 +10078,7 @@
 	nla_nest_end(msg, nlbssids);
 	nla_nest_end(msg, params);
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 
 fail:
 	nlmsg_free(msg);
@@ -9998,7 +10116,7 @@
 	}
 	nla_nest_end(msg, params);
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 }
 
 #endif /* CONFIG_DRIVER_NL80211_QCA */
@@ -10009,10 +10127,45 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int new_addr = addr != NULL;
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+	struct nl_msg *msg;
+	struct nlattr *params;
+	int ret;
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+	wpa_printf(MSG_DEBUG, "Enter: %s", __FUNCTION__);
 
 	if (TEST_FAIL())
 		return -1;
+	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+		if (!addr ) {
+			addr = drv->global->p2p_perm_addr;
+		}
 
+		if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) ||
+			nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_BRCM) ||
+			nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+				BRCM_VENDOR_SUBCMD_SET_MAC) ||
+			!(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+			nla_put(msg, BRCM_ATTR_DRIVER_MAC_ADDR, ETH_ALEN, addr)) {
+			wpa_printf(MSG_ERROR, "failed to put p2p randmac");
+			nl80211_nlmsg_clear(msg);
+			nlmsg_free(msg);
+			return -ENOBUFS;
+		}
+		nla_nest_end(msg, params);
+
+		ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
+		if (ret) {
+			wpa_printf(MSG_ERROR, "nl80211: p2p set macaddr failed: ret=%d (%s)",
+				ret, strerror(-ret));
+		}
+		memcpy(bss->addr, addr, ETH_ALEN);
+		return ret;
+#else
+		return -ENOTSUP;
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+	}
 	if (!addr)
 		addr = drv->perm_addr;
 
@@ -10022,27 +10175,26 @@
 	if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr) < 0)
 	{
 		wpa_printf(MSG_DEBUG,
-			   "nl80211: failed to set_mac_addr for %s to " MACSTR,
-			   bss->ifname, MAC2STR(addr));
+			"nl80211: failed to set_mac_addr for %s to " MACSTR,
+			bss->ifname, MAC2STR(addr));
 		if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
-					  1) < 0) {
+			1) < 0) {
 			wpa_printf(MSG_DEBUG,
-				   "nl80211: Could not restore interface UP after failed set_mac_addr");
+				"nl80211: Could not restore interface UP after failed set_mac_addr");
 		}
 		return -1;
 	}
 
 	wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
-		   bss->ifname, MAC2STR(addr));
+		bss->ifname, MAC2STR(addr));
 	drv->addr_changed = new_addr;
 	os_memcpy(bss->addr, addr, ETH_ALEN);
 
 	if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
 	{
 		wpa_printf(MSG_DEBUG,
-			   "nl80211: Could not restore interface UP after set_mac_addr");
+			"nl80211: Could not restore interface UP after set_mac_addr");
 	}
-
 	return 0;
 }
 
@@ -10167,7 +10319,7 @@
 		goto fail;
 
 	ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 1,
-				       NULL, NULL);
+				       NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)",
@@ -10225,7 +10377,7 @@
 	wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
 	ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 0,
-				       NULL, NULL);
+				       NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
 			   ret, strerror(-ret));
@@ -10259,7 +10411,7 @@
 		return -ENOBUFS;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: mesh link probe to " MACSTR
 			   " failed: ret=%d (%s)",
@@ -10671,7 +10823,7 @@
 		   params->hw_mode, params->ht_enabled, params->ht40_enabled,
 		   params->vht_enabled, params->ch_width, params->edmg_enabled);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Failed to invoke driver ACS function: %s",
@@ -10718,7 +10870,7 @@
 	}
 	nla_nest_end(msg, data);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Driver setband function failed: %s",
@@ -10851,7 +11003,8 @@
 	nla_nest_end(msg, params);
 
 	os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
-	ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param);
+	ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param,
+				 NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_ERROR,
 			   "%s: err in send_and_recv_msgs", __func__);
@@ -10903,7 +11056,7 @@
 	}
 	nla_nest_end(msg, params);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
@@ -10959,7 +11112,7 @@
 		goto fail;
 
 	nla_nest_end(msg, container);
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
@@ -10994,7 +11147,7 @@
 		return -1;
 	}
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 }
 
 
@@ -11033,7 +11186,7 @@
 
 	nla_nest_end(msg, params);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_ERROR,
@@ -11226,7 +11379,7 @@
 
 	ret = send_and_recv_msgs(drv, msg,
 				 nl80211_get_bss_transition_status_handler,
-				 info);
+				 info, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_ERROR,
@@ -11279,7 +11432,7 @@
 
 	nla_nest_end(msg, attr);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_ERROR,
@@ -11482,7 +11635,7 @@
 	    nl80211_put_fils_connect_params(drv, params, msg))
 		goto fail;
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret)
 		wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -11525,7 +11678,7 @@
 	    (params->bssid &&
 	     nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)))
 		goto fail;
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
@@ -11565,7 +11718,7 @@
 		bss->added_if_into_bridge = 0;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (!ret) {
 		if (bridge_ifname[0] && val &&
@@ -11731,7 +11884,7 @@
 	.get_bss_transition_status = nl80211_get_bss_transition_status,
 	.ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
 #endif /* CONFIG_MBO */
-	.set_bssid_blacklist = nl80211_set_bssid_blacklist,
+	.set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow,
 	.add_sta_node = nl80211_add_sta_node,
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 895f9d7..0db7d55 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -33,8 +33,8 @@
 	struct nl_sock *nl;
 	int nl80211_id;
 	int ioctl_sock; /* socket for ioctl() use */
-
 	struct nl_sock *nl_event;
+	u8 p2p_perm_addr[ETH_ALEN];
 };
 
 struct nl80211_wiphy_data {
@@ -173,6 +173,7 @@
 	unsigned int add_sta_node_vendor_cmd_avail:1;
 	unsigned int control_port_ap:1;
 	unsigned int multicast_registrations:1;
+	unsigned int no_rrm:1;
 
 	u64 vendor_scan_cookie;
 	u64 remain_on_chan_cookie;
@@ -180,6 +181,7 @@
 #define MAX_SEND_FRAME_COOKIES 20
 	u64 send_frame_cookies[MAX_SEND_FRAME_COOKIES];
 	unsigned int num_send_frame_cookies;
+	u64 eapol_tx_cookie;
 
 	unsigned int last_mgmt_freq;
 
@@ -230,7 +232,9 @@
 struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
 int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg,
 		       int (*valid_handler)(struct nl_msg *, void *),
-		       void *valid_data);
+		       void *valid_data,
+		       int (*ack_handler_custom)(struct nl_msg *, void *),
+		       void *ack_data);
 struct nl_sock * get_connect_handle(struct i802_bss *bss);
 int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
 			 const char *ifname, enum nl80211_iftype iftype,
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 435df11..416531f 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -51,7 +51,8 @@
 		return 0;
 	}
 
-	if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0)
+	if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat,
+			       NULL, NULL) == 0)
 		return feat;
 
 	return 0;
@@ -621,6 +622,10 @@
 	if (ext_feature_isset(ext_features, len,
 			      NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH))
 		capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_RX;
+	if (ext_feature_isset(
+		    ext_features, len,
+		    NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS;
 
 	if (ext_feature_isset(ext_features, len,
 			      NL80211_EXT_FEATURE_VLAN_OFFLOAD))
@@ -1068,7 +1073,7 @@
 		return -1;
 	}
 
-	if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
+	if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info, NULL, NULL))
 		return -1;
 
 	if (info->auth_supported)
@@ -1174,7 +1179,8 @@
 		return;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability);
+	ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability,
+				 NULL, NULL);
 	if (!ret && dfs_capability)
 		drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
 }
@@ -1261,7 +1267,8 @@
 
 	os_memset(&info, 0, sizeof(info));
 	info.capa = &drv->capa;
-	ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
+	ret = send_and_recv_msgs(drv, msg, features_info_handler, &info,
+				 NULL, NULL);
 	if (ret || !info.flags)
 		return;
 
@@ -1806,6 +1813,13 @@
 			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]),
 			  len);
 	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]) {
+		u16 capa;
+
+		capa = nla_get_u16(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]);
+		he_capab->he_6ghz_capa = le_to_host16(capa);
+	}
 }
 
 
@@ -1948,7 +1962,10 @@
 	for (m = 0; m < *num_modes; m++) {
 		if (!modes[m].num_channels)
 			continue;
-		if (modes[m].channels[0].freq < 4000) {
+		if (modes[m].channels[0].freq < 2000) {
+			modes[m].num_channels = 0;
+			continue;
+		} else if (modes[m].channels[0].freq < 4000) {
 			modes[m].mode = HOSTAPD_MODE_IEEE80211B;
 			for (i = 0; i < modes[m].num_rates; i++) {
 				if (modes[m].rates[i] > 200) {
@@ -2336,7 +2353,8 @@
 		}
 	}
 
-	return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
+	return send_and_recv_msgs(drv, msg, nl80211_get_reg, results,
+				  NULL, NULL);
 }
 
 
@@ -2425,7 +2443,8 @@
 		return NULL;
 	}
 
-	if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+	if (send_and_recv_msgs(drv, msg, phy_info_handler, &result,
+			       NULL, NULL) == 0) {
 		struct hostapd_hw_modes *modes;
 
 		nl80211_set_regulatory_flags(drv, &result);
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 0454d29..680d426 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -22,6 +22,12 @@
 #include "common/brcm_vendor.h"
 #endif /* CONFIG_DRIVER_NL80211_BRCM */
 
+static void
+nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+				     const u8 *frame, size_t len,
+				     struct nlattr *ack, struct nlattr *cookie);
+
+
 static const char * nl80211_command_to_string(enum nl80211_commands cmd)
 {
 #define C2S(x) case x: return #x;
@@ -140,6 +146,8 @@
 	C2S(NL80211_CMD_CONTROL_PORT_FRAME)
 	C2S(NL80211_CMD_UPDATE_OWE_INFO)
 	C2S(NL80211_CMD_UNPROT_BEACON)
+	C2S(NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS)
+
 	default:
 		return "NL80211_CMD_UNKNOWN";
 	}
@@ -700,6 +708,16 @@
 		   WLAN_FC_GET_STYPE(fc), (long long unsigned int) cookie_val,
 		   cookie ? "" : "(N/A)", ack != NULL);
 
+	if (cookie_val && cookie_val == drv->eapol_tx_cookie &&
+	    len >= ETH_HLEN &&
+	    WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Work around misdelivered control port TX status for EAPOL");
+		nl80211_control_port_frame_tx_status(drv, frame, len, ack,
+						     cookie);
+		return;
+	}
+
 	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
 		return;
 
@@ -2653,6 +2671,29 @@
 }
 
 
+static void
+nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+				     const u8 *frame, size_t len,
+				     struct nlattr *ack, struct nlattr *cookie)
+{
+	union wpa_event_data event;
+
+	if (!cookie || len < ETH_HLEN)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Control port TX status (ack=%d), cookie=%llu",
+		   ack != NULL, (long long unsigned int) nla_get_u64(cookie));
+
+	os_memset(&event, 0, sizeof(event));
+	event.eapol_tx_status.dst = frame;
+	event.eapol_tx_status.data = frame + ETH_HLEN;
+	event.eapol_tx_status.data_len = len - ETH_HLEN;
+	event.eapol_tx_status.ack = ack != NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+}
+
+
 static void do_process_drv_event(struct i802_bss *bss, int cmd,
 				 struct nlattr **tb)
 {
@@ -2871,6 +2912,15 @@
 			mlme_event_unprot_beacon(drv, nla_data(frame),
 						 nla_len(frame));
 		break;
+	case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
+		if (!frame)
+			break;
+		nl80211_control_port_frame_tx_status(drv,
+						     nla_data(frame),
+						     nla_len(frame),
+						     tb[NL80211_ATTR_ACK],
+						     tb[NL80211_ATTR_COOKIE]);
+		break;
 	default:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
 			"(cmd=%d)", cmd);
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index dc91a29..233175d 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -82,7 +82,8 @@
 
 	os_memset(info, 0, sizeof(*info));
 	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
-	return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info);
+	return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info,
+				  NULL, NULL);
 }
 
 
@@ -94,7 +95,7 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
 	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
 			   ret, strerror(-ret));
@@ -125,7 +126,7 @@
 
 	nla_nest_end(msg, params);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_INFO,
@@ -365,7 +366,7 @@
 			goto fail;
 	}
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
@@ -618,7 +619,7 @@
 			params->sched_scan_start_delay))
 		goto fail;
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 
 	/* TODO: if we get an error here, we should fall back to normal scan */
 
@@ -655,7 +656,7 @@
 #endif /* ANDROID */
 
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Sched scan stop failed: ret=%d (%s)",
@@ -944,7 +945,7 @@
 
 	arg.drv = drv;
 	arg.res = res;
-	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg, NULL, NULL);
 	if (ret == -EAGAIN) {
 		count++;
 		if (count >= 10) {
@@ -1028,7 +1029,8 @@
 	ctx.idx = 0;
 	msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
 	if (msg)
-		send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx);
+		send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx,
+				   NULL, NULL);
 }
 
 
@@ -1221,7 +1223,8 @@
 
 	nla_nest_end(msg, attr);
 
-	ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie);
+	ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie,
+				 NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
@@ -1284,7 +1287,7 @@
 
 	nla_nest_end(msg, attr);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_ERROR,
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 5e500d7..dad8c8f 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -296,13 +296,14 @@
  *	to get a list of all present wiphys.
  * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
  *	%NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
- *	%NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the
- *	attributes determining the channel width; this is used for setting
- *	monitor mode channel),  %NL80211_ATTR_WIPHY_RETRY_SHORT,
- *	%NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
- *	and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
- *	However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
- *	instead, the support here is for backward compatibility only.
+ *	%NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ,
+ *	%NL80211_ATTR_WIPHY_FREQ_OFFSET (and the attributes determining the
+ *	channel width; this is used for setting monitor mode channel),
+ *	%NL80211_ATTR_WIPHY_RETRY_SHORT, %NL80211_ATTR_WIPHY_RETRY_LONG,
+ *	%NL80211_ATTR_WIPHY_FRAG_THRESHOLD, and/or
+ *	%NL80211_ATTR_WIPHY_RTS_THRESHOLD.  However, for setting the channel,
+ *	see %NL80211_CMD_SET_CHANNEL instead, the support here is for backward
+ *	compatibility only.
  * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
  *	or rename notification. Has attributes %NL80211_ATTR_WIPHY and
  *	%NL80211_ATTR_WIPHY_NAME.
@@ -351,7 +352,8 @@
  *	%NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
  *	%NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
  *	The channel to use can be set on the interface or be given using the
- *	%NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width.
+ *	%NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_FREQ_OFFSET, and the
+ *	attributes determining channel width.
  * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
  * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
  * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
@@ -536,11 +538,12 @@
  *	interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and
  *	BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
  *	the SSID (mainly for association, but is included in authentication
- *	request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used
- *	to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE
- *	is used to specify the authentication type. %NL80211_ATTR_IE is used to
- *	define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs)
- *	to be added to the frame.
+ *	request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ +
+ *	%NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequence of the
+ *	channel in MHz. %NL80211_ATTR_AUTH_TYPE is used to specify the
+ *	authentication type. %NL80211_ATTR_IE is used to define IEs
+ *	(VendorSpecificInfo, but also including RSN IE and FT IEs) to be added
+ *	to the frame.
  *	When used as an event, this reports reception of an Authentication
  *	frame in station and IBSS modes when the local MLME processed the
  *	frame, i.e., it was for the local STA and was received in correct
@@ -595,8 +598,9 @@
  *	requests to connect to a specified network but without separating
  *	auth and assoc steps. For this, you need to specify the SSID in a
  *	%NL80211_ATTR_SSID attribute, and can optionally specify the association
- *	IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
- *	%NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ *	IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE,
+ *	%NL80211_ATTR_USE_MFP, %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ,
+ *	%NL80211_ATTR_WIPHY_FREQ_OFFSET, %NL80211_ATTR_CONTROL_PORT,
  *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
  *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
  *	%NL80211_ATTR_CONTROL_PORT_OVER_NL80211, %NL80211_ATTR_MAC_HINT, and
@@ -1160,6 +1164,12 @@
  *	dropped because it did not include a valid MME MIC while beacon
  *	protection was enabled (BIGTK configured in station mode).
  *
+ * @NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS: Report TX status of a control
+ *	port frame transmitted with %NL80211_CMD_CONTROL_PORT_FRAME.
+ *	%NL80211_ATTR_COOKIE identifies the TX command and %NL80211_ATTR_FRAME
+ *	includes the contents of the frame. %NL80211_ATTR_ACK flag is included
+ *	if the recipient acknowledged the frame.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1388,6 +1398,8 @@
 
 	NL80211_CMD_UNPROT_BEACON,
 
+	NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -1433,7 +1445,8 @@
  *	of &enum nl80211_chan_width, describing the channel width. See the
  *	documentation of the enum for more information.
  * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the
- *	channel, used for anything but 20 MHz bandwidth
+ *	channel, used for anything but 20 MHz bandwidth. In S1G this is the
+ *	operating channel center frequency.
  * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the
  *	channel, used only for 80+80 MHz bandwidth
  * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
@@ -2480,9 +2493,17 @@
  *	entry without having to force a disconnection after the PMK timeout. If
  *	no roaming occurs between the reauth threshold and PMK expiration,
  *	disassociation is still forced.
- *
  * @NL80211_ATTR_RECEIVE_MULTICAST: multicast flag for the
  *	%NL80211_CMD_REGISTER_FRAME command, see the description there.
+ * @NL80211_ATTR_WIPHY_FREQ_OFFSET: offset of the associated
+ *	%NL80211_ATTR_WIPHY_FREQ in positive KHz. Only valid when supplied with
+ *	an %NL80211_ATTR_WIPHY_FREQ_OFFSET.
+ * @NL80211_ATTR_CENTER_FREQ1_OFFSET: Center frequency offset in KHz for the
+ *	first channel segment specified in %NL80211_ATTR_CENTER_FREQ1.
+ * @NL80211_ATTR_SCAN_FREQ_KHZ: nested attribute with KHz frequencies
+ *
+ * @NL80211_ATTR_HE_6GHZ_CAPABILITY: HE 6 GHz Band Capability element (from
+ *	association request when used with NL80211_CMD_NEW_STATION).
  *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
@@ -2960,6 +2981,11 @@
 	NL80211_ATTR_PMK_REAUTH_THRESHOLD,
 
 	NL80211_ATTR_RECEIVE_MULTICAST,
+	NL80211_ATTR_WIPHY_FREQ_OFFSET,
+	NL80211_ATTR_CENTER_FREQ1_OFFSET,
+	NL80211_ATTR_SCAN_FREQ_KHZ,
+
+	NL80211_ATTR_HE_6GHZ_CAPABILITY,
 
 	/* add attributes here, update the policy in nl80211.c */
 
@@ -3539,6 +3565,8 @@
  *     defined in HE capabilities IE
  * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band HE capability attribute currently
  *     defined
+ * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
+ *	given for all 6 GHz band channels
  * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_band_iftype_attr {
@@ -3549,6 +3577,7 @@
 	NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY,
 	NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
 	NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
+	NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
 
 	/* keep last */
 	__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
@@ -3682,6 +3711,7 @@
  *	(see &enum nl80211_wmm_rule)
  * @NL80211_FREQUENCY_ATTR_NO_HE: HE operation is not allowed on this channel
  *	in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_OFFSET: frequency offset in KHz
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *	currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -3712,6 +3742,7 @@
 	NL80211_FREQUENCY_ATTR_NO_10MHZ,
 	NL80211_FREQUENCY_ATTR_WMM,
 	NL80211_FREQUENCY_ATTR_NO_HE,
+	NL80211_FREQUENCY_ATTR_OFFSET,
 
 	/* keep last */
 	__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -4482,6 +4513,7 @@
  * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update.
  *	Contains a nested array of signal strength attributes (u8, dBm),
  *	using the nesting index as the antenna number.
+ * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -4506,6 +4538,7 @@
 	NL80211_BSS_PARENT_TSF,
 	NL80211_BSS_PARENT_BSSID,
 	NL80211_BSS_CHAIN_SIGNAL,
+	NL80211_BSS_FREQUENCY_OFFSET,
 
 	/* keep last */
 	__NL80211_BSS_AFTER_LAST,
@@ -4816,6 +4849,17 @@
 	NL80211_TID_CONFIG_DISABLE,
 };
 
+/* enum nl80211_tx_rate_setting - TX rate configuration type
+ * @NL80211_TX_RATE_AUTOMATIC: automatically determine TX rate
+ * @NL80211_TX_RATE_LIMITED: limit the TX rate by the TX rate parameter
+ * @NL80211_TX_RATE_FIXED: fix TX rate to the TX rate parameter
+ */
+enum nl80211_tx_rate_setting {
+	NL80211_TX_RATE_AUTOMATIC,
+	NL80211_TX_RATE_LIMITED,
+	NL80211_TX_RATE_FIXED,
+};
+
 /* enum nl80211_tid_config_attr - TID specific configuration.
  * @NL80211_TID_CONFIG_ATTR_PAD: pad attribute for 64-bit values
  * @NL80211_TID_CONFIG_ATTR_VIF_SUPP: a bitmap (u64) of attributes supported
@@ -4823,12 +4867,10 @@
  *	(%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE).
  * @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but
  *	per peer instead.
- * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribute, if no peer
- *	is selected, if set indicates that the new configuration overrides
- *	all previous peer configurations, otherwise previous peer specific
- *	configurations should be left untouched. If peer is selected then
- *	it will reset particular TID configuration of that peer and it will
- *	not accept other TID config attributes along with peer.
+ * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if set indicates
+ *	that the new configuration overrides all previous peer
+ *	configurations, otherwise previous peer specific configurations
+ *	should be left untouched.
  * @NL80211_TID_CONFIG_ATTR_TIDS: a bitmask value of TIDs (bit 0 to 7)
  *	Its type is u16.
  * @NL80211_TID_CONFIG_ATTR_NOACK: Configure ack policy for the TID.
@@ -4844,12 +4886,23 @@
  *	&NL80211_CMD_SET_TID_CONFIG. Its type is u8, min value is 1 and
  *	the max value is advertised by the driver in this attribute on
  *	output in wiphy capabilities.
- * @NL80211_TID_CONFIG_ATTR_AMPDU_CTRL: Enable/Disable aggregation for the TIDs
- *	specified in %NL80211_TID_CONFIG_ATTR_TIDS. Its type is u8, using
- *	the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_AMPDU_CTRL: Enable/Disable MPDU aggregation
+ *	for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS.
+ *	Its type is u8, using the values from &nl80211_tid_config.
  * @NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL: Enable/Disable RTS_CTS for the TIDs
  *	specified in %NL80211_TID_CONFIG_ATTR_TIDS. It is u8 type, using
  *	the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_AMSDU_CTRL: Enable/Disable MSDU aggregation
+ *	for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS.
+ *	Its type is u8, using the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE: This attribute will be useful
+ *	to notfiy the driver that what type of txrate should be used
+ *	for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS. using
+ *	the values form &nl80211_tx_rate_setting.
+ * @NL80211_TID_CONFIG_ATTR_TX_RATE: Data frame TX rate mask should be applied
+ *	with the parameters passed through %NL80211_ATTR_TX_RATES.
+ *	configuration is applied to the data frame for the tid to that connected
+ *	station.
  */
 enum nl80211_tid_config_attr {
 	__NL80211_TID_CONFIG_ATTR_INVALID,
@@ -4863,6 +4916,9 @@
 	NL80211_TID_CONFIG_ATTR_RETRY_LONG,
 	NL80211_TID_CONFIG_ATTR_AMPDU_CTRL,
 	NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL,
+	NL80211_TID_CONFIG_ATTR_AMSDU_CTRL,
+	NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE,
+	NL80211_TID_CONFIG_ATTR_TX_RATE,
 
 	/* keep last */
 	__NL80211_TID_CONFIG_ATTR_AFTER_LAST,
@@ -5340,6 +5396,8 @@
 
 #define NL80211_KCK_LEN			16
 #define NL80211_KEK_LEN			16
+#define NL80211_KCK_EXT_LEN		24
+#define NL80211_KEK_EXT_LEN		32
 #define NL80211_REPLAY_CTR_LEN		8
 
 /**
@@ -5348,6 +5406,7 @@
  * @NL80211_REKEY_DATA_KEK: key encryption key (binary)
  * @NL80211_REKEY_DATA_KCK: key confirmation key (binary)
  * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary)
+ * @NL80211_REKEY_DATA_AKM: AKM data (OUI, suite type)
  * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal)
  * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal)
  */
@@ -5356,6 +5415,7 @@
 	NL80211_REKEY_DATA_KEK,
 	NL80211_REKEY_DATA_KCK,
 	NL80211_REKEY_DATA_REPLAY_CTR,
+	NL80211_REKEY_DATA_AKM,
 
 	/* keep last */
 	NUM_NL80211_REKEY_DATA,
@@ -5705,6 +5765,14 @@
  * @NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS: management frame registrations
  *	are possible for multicast frames and those will be reported properly.
  *
+ * @NL80211_EXT_FEATURE_SCAN_FREQ_KHZ: This driver supports receiving and
+ *	reporting scan request with %NL80211_ATTR_SCAN_FREQ_KHZ. In order to
+ *	report %NL80211_ATTR_SCAN_FREQ_KHZ, %NL80211_SCAN_FLAG_FREQ_KHZ must be
+ *	included in the scan request.
+ *
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS: The driver
+ *	can report tx status for control port over nl80211 tx operations.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -5758,6 +5826,8 @@
 	NL80211_EXT_FEATURE_DEL_IBSS_STA,
 	NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS,
 	NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT,
+	NL80211_EXT_FEATURE_SCAN_FREQ_KHZ,
+	NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -5869,6 +5939,9 @@
  * @NL80211_SCAN_FLAG_MIN_PREQ_CONTENT: minimize probe request content to
  *	only have supported rates and no additional capabilities (unless
  *	added by userspace explicitly.)
+ * @NL80211_SCAN_FLAG_FREQ_KHZ: report scan results with
+ *	%NL80211_ATTR_SCAN_FREQ_KHZ. This also means
+ *	%NL80211_ATTR_SCAN_FREQUENCIES will not be included.
  */
 enum nl80211_scan_flags {
 	NL80211_SCAN_FLAG_LOW_PRIORITY				= 1<<0,
@@ -5884,6 +5957,7 @@
 	NL80211_SCAN_FLAG_HIGH_ACCURACY				= 1<<10,
 	NL80211_SCAN_FLAG_RANDOM_SN				= 1<<11,
 	NL80211_SCAN_FLAG_MIN_PREQ_CONTENT			= 1<<12,
+	NL80211_SCAN_FLAG_FREQ_KHZ				= 1<<13,
 };
 
 /**
diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile
index f00b438..fd058a0 100644
--- a/src/eap_common/Makefile
+++ b/src/eap_common/Makefile
@@ -1,13 +1,3 @@
-all: libeap_common.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeap_common.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 LIB_OBJS= \
 	chap.o \
 	eap_common.o \
@@ -25,7 +15,4 @@
 	eap_wsc_common.o \
 	ikev2_common.o
 
-libeap_common.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/eap_peer/.gitignore b/src/eap_peer/.gitignore
new file mode 100644
index 0000000..140f8cf
--- /dev/null
+++ b/src/eap_peer/.gitignore
@@ -0,0 +1 @@
+*.so
diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile
index 6531ccd..bdbead6 100644
--- a/src/eap_peer/Makefile
+++ b/src/eap_peer/Makefile
@@ -1,23 +1,13 @@
-all: libeap_peer.a
-
-clean:
-	rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov libeap_peer.a
-
-install:
-	if ls *.so >/dev/null 2>&1; then \
-		install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
-		cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
-	; fi
-
-include ../lib.rules
-
 CFLAGS += -DIEEE8021X_EAPOL
 
 LIB_OBJS= \
 	eap.o \
 	eap_methods.o
 
-libeap_peer.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
+include ../lib.rules
 
--include $(OBJS:%.o=%.d)
+install:
+	if ls *.so >/dev/null 2>&1; then \
+		install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
+		cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
+	; fi
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index eaa1ad7..0986627 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -520,6 +520,12 @@
 	wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
 			       EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START);
+	if (identity) {
+		wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
+				  identity, identity_len);
+		eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+				identity, identity_len);
+	}
 	if (!data->reauth) {
 		wpa_hexdump(MSG_DEBUG, "   AT_NONCE_MT",
 			    data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
@@ -531,13 +537,6 @@
 				data->selected_version, NULL, 0);
 	}
 
-	if (identity) {
-		wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
-				  identity, identity_len);
-		eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
-				identity, identity_len);
-	}
-
 	resp = eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
 	if (resp)
 		eap_sim_state(data, START_DONE);
diff --git a/src/eap_peer/eap_teap.c b/src/eap_peer/eap_teap.c
index 76179a3..e8cc784 100644
--- a/src/eap_peer/eap_teap.c
+++ b/src/eap_peer/eap_teap.c
@@ -1388,6 +1388,15 @@
 			   "EAP-TEAP: PAC used - server may decide to skip inner authentication");
 		ret->methodState = METHOD_MAY_CONT;
 		ret->decision = DECISION_COND_SUCC;
+	} else if (data->result_success_done &&
+		   tls_connection_get_own_cert_used(data->ssl.conn) &&
+		   eap_teap_derive_msk(data) == 0) {
+		/* Assume the server might accept authentication without going
+		 * through inner authentication. */
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Client certificate used - server may decide to skip inner authentication");
+		ret->methodState = METHOD_MAY_CONT;
+		ret->decision = DECISION_COND_SUCC;
 	}
 
 	if (tlv.pac) {
diff --git a/src/eap_server/Makefile b/src/eap_server/Makefile
index 1172b72..cc9b76d 100644
--- a/src/eap_server/Makefile
+++ b/src/eap_server/Makefile
@@ -1,13 +1,3 @@
-all: libeap_server.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeap_server.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 CFLAGS += -DCONFIG_HS20
 
 LIB_OBJS= \
@@ -15,7 +5,4 @@
 	eap_server_identity.o \
 	eap_server_methods.o
 
-libeap_server.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/eap_server/eap_server_teap.c b/src/eap_server/eap_server_teap.c
index d7b1b09..691b44a 100644
--- a/src/eap_server/eap_server_teap.c
+++ b/src/eap_server/eap_server_teap.c
@@ -64,7 +64,7 @@
 	struct wpabuf *pending_phase2_resp;
 	struct wpabuf *server_outer_tlvs;
 	struct wpabuf *peer_outer_tlvs;
-	u8 *identity; /* from PAC-Opaque */
+	u8 *identity; /* from PAC-Opaque or client certificate */
 	size_t identity_len;
 	int eap_seq;
 	int tnc_started;
@@ -365,7 +365,9 @@
 	data->teap_version = EAP_TEAP_VERSION;
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TEAP)) {
+	if (eap_server_tls_ssl_init(sm, &data->ssl,
+				    sm->cfg->eap_teap_auth == 2 ? 2 : 0,
+				    EAP_TYPE_TEAP)) {
 		wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL.");
 		eap_teap_reset(sm, data);
 		return NULL;
@@ -502,6 +504,19 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 1 done, starting Phase 2");
 
+	if (!data->identity && sm->cfg->eap_teap_auth == 2) {
+		const char *subject;
+
+		subject = tls_connection_get_peer_subject(data->ssl.conn);
+		if (subject) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Peer subject from Phase 1 client certificate: '%s'",
+				   subject);
+			data->identity = (u8 *) os_strdup(subject);
+			data->identity_len = os_strlen(subject);
+		}
+	}
+
 	data->tls_cs = tls_connection_get_cipher_suite(data->ssl.conn);
 	wpa_printf(MSG_DEBUG, "EAP-TEAP: TLS cipher suite 0x%04x",
 		   data->tls_cs);
@@ -1775,9 +1790,10 @@
 			next_vendor = EAP_VENDOR_IETF;
 			next_type = EAP_TYPE_NONE;
 			eap_teap_state(data, PHASE2_METHOD);
-		} else if (sm->cfg->eap_teap_pac_no_inner) {
+		} else if (sm->cfg->eap_teap_pac_no_inner ||
+			sm->cfg->eap_teap_auth == 2) {
 			wpa_printf(MSG_DEBUG,
-				   "EAP-TEAP: Used PAC and identity already known - skip inner auth");
+				   "EAP-TEAP: Used PAC or client certificate and identity already known - skip inner auth");
 			data->skipped_inner_auth = 1;
 			/* FIX: Need to derive CMK here. However, how is that
 			 * supposed to be done? RFC 7170 does not tell that for
diff --git a/src/eapol_auth/Makefile b/src/eapol_auth/Makefile
index 7b927a1..c82042f 100644
--- a/src/eapol_auth/Makefile
+++ b/src/eapol_auth/Makefile
@@ -1,16 +1,2 @@
-all: libeapol_auth.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeapol_auth.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 LIB_OBJS = eapol_auth_sm.o eapol_auth_dump.o
-
-libeapol_auth.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/eapol_supp/Makefile b/src/eapol_supp/Makefile
index 80db9d4..67a1185 100644
--- a/src/eapol_supp/Makefile
+++ b/src/eapol_supp/Makefile
@@ -1,18 +1,5 @@
-all: libeapol_supp.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeapol_supp.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 CFLAGS += -DIEEE8021X_EAPOL
 
 LIB_OBJS = eapol_supp_sm.o
 
-libeapol_supp.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile
index 47925b7..c616626 100644
--- a/src/l2_packet/Makefile
+++ b/src/l2_packet/Makefile
@@ -1,16 +1,3 @@
-all: libl2_packet.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libl2_packet.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 LIB_OBJS = l2_packet_linux.o
 
-libl2_packet.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/lib.rules b/src/lib.rules
index a463154..947617b 100644
--- a/src/lib.rules
+++ b/src/lib.rules
@@ -1,10 +1,8 @@
-ifndef CC
-CC=gcc
-endif
-
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
+_LIBMK := $(lastword $(wordlist 1,$(shell expr $(words $(MAKEFILE_LIST)) - 1),$(MAKEFILE_LIST)))
+_LIBNAME := $(notdir $(patsubst %/,%,$(dir $(abspath $(_LIBMK)))))
+ALL := $(OUT)lib$(_LIBNAME).a
+LIB_RULES := $(lastword $(MAKEFILE_LIST))
+include $(dir $(LIB_RULES))build.rules
 
 ifdef TEST_FUZZ
 CFLAGS += -DCONFIG_NO_RANDOM_POOL
@@ -14,18 +12,18 @@
 CFLAGS += $(FUZZ_CFLAGS)
 CFLAGS += -I.. -I../utils
 
+_OBJS_VAR := LIB_OBJS
+include ../objs.mk
 
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-ifeq ($(QUIET), 1)
-Q=@
-E=true
-endif
+$(ALL): $(LIB_OBJS)
+	@$(E) "  AR  $(notdir $@)"
+	$(Q)$(AR) crT $@ $?
 
-%.o: %.c
-	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
-	@$(E) "  CC " $<
+install-default:
+	@echo Nothing to be made.
+
+%: %-default
+	@true
+
+clean: common-clean
+	$(Q)rm -f *~ *.o *.d *.gcno *.gcda *.gcov $(ALL)
diff --git a/src/objs.mk b/src/objs.mk
new file mode 100644
index 0000000..a3040b2
--- /dev/null
+++ b/src/objs.mk
@@ -0,0 +1,3 @@
+$(_OBJS_VAR) := $(call BUILDOBJ,$($(_OBJS_VAR)))
+-include $(filter-out %.a,$($(_OBJS_VAR):%.o=%.d))
+_DIRS += $(dir $($(_OBJS_VAR)))
diff --git a/src/p2p/Makefile b/src/p2p/Makefile
index 5587fcf..4d16180 100644
--- a/src/p2p/Makefile
+++ b/src/p2p/Makefile
@@ -1,13 +1,3 @@
-all: libp2p.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libp2p.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 CFLAGS += -DCONFIG_WIFI_DISPLAY
 CFLAGS += -DCONFIG_WPS_NFC
 
@@ -23,7 +13,4 @@
 	p2p_sd.o \
 	p2p_utils.o
 
-libp2p.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 9c73885..6ddfb1e 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -3979,6 +3979,7 @@
 	}
 
 	p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation");
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
 	p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
 	p2p_listen_in_find(p2p, 0);
 }
diff --git a/src/radius/Makefile b/src/radius/Makefile
index 3ad4751..8cfb33d 100644
--- a/src/radius/Makefile
+++ b/src/radius/Makefile
@@ -1,14 +1,3 @@
-all: libradius.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libradius.a
-
-install:
-	@echo Nothing to be made.
-
-
-include ../lib.rules
-
 CFLAGS += -DCONFIG_IPV6
 
 LIB_OBJS= \
@@ -17,7 +6,4 @@
 	radius_das.o \
 	radius_server.o
 
-libradius.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
index eea0efb..d14d736 100644
--- a/src/rsn_supp/Makefile
+++ b/src/rsn_supp/Makefile
@@ -1,13 +1,3 @@
-all: librsn_supp.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov librsn_supp.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 CFLAGS += -DCONFIG_IEEE80211R
 CFLAGS += -DCONFIG_TDLS
 CFLAGS += -DCONFIG_WNM
@@ -21,7 +11,4 @@
 	wpa.o \
 	wpa_ie.o
 
-librsn_supp.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index e46c89a..b124dd9 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -272,7 +272,8 @@
 			 entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
 			 entry->pmk, entry->pmk_len,
 			 pmksa->sm->dot11RSNAConfigPMKLifetime,
-			 pmksa->sm->dot11RSNAConfigPMKReauthThreshold);
+			 pmksa->sm->dot11RSNAConfigPMKReauthThreshold,
+			 entry->akmp);
 
 	return entry;
 }
@@ -488,6 +489,9 @@
 {
 	if (sm == NULL)
 		return;
+	if (sm->cur_pmksa)
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Clear current PMKSA entry selection");
 	sm->cur_pmksa = NULL;
 }
 
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index d4d1307..1a38bf6 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -349,7 +349,8 @@
 		 * PMKIDs again, so report the existing data now. */
 		if (p) {
 			wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid,
-					 NULL, p->pmk, p->pmk_len, 0, 0);
+					 NULL, p->pmk, p->pmk_len, 0, 0,
+					 p->akmp);
 		}
 
 		dl_list_del(&candidate->list);
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 7b47e3a..eff8cd8 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -2807,6 +2807,11 @@
 	if (sm == NULL)
 		return -1;
 
+	if (sm->l2_tdls) {
+		l2_packet_deinit(sm->l2_tdls);
+		sm->l2_tdls = NULL;
+	}
+
 	sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
 				     sm->ifname,
 				     sm->own_addr,
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index b9b1f0f..6c6de80 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -22,6 +22,7 @@
 #include "common/ieee802_11_common.h"
 #include "common/ocv.h"
 #include "common/dpp.h"
+#include "common/wpa_ctrl.h"
 #include "eap_common/eap_defs.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "drivers/driver.h"
@@ -756,6 +757,14 @@
 				   "Failed to get channel info for OCI element in EAPOL-Key 2/4");
 			goto failed;
 		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (sm->oci_freq_override_eapol) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI KDE frequency %d -> %d MHz",
+				   ci.frequency, sm->oci_freq_override_eapol);
+			ci.frequency = sm->oci_freq_override_eapol;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 		if (ocv_insert_oci_kde(&ci, &pos) < 0)
 			goto failed;
@@ -1708,9 +1717,10 @@
 
 		if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
 					 channel_width_to_int(ci.chanwidth),
-					 ci.seg1_idx) != 0) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s",
-				ocv_errorstr);
+					 ci.seg1_idx) != OCI_SUCCESS) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+				"addr=" MACSTR " frame=eapol-key-m3 error=%s",
+				MAC2STR(sm->bssid), ocv_errorstr);
 			return;
 		}
 	}
@@ -1855,9 +1865,10 @@
 
 		if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
 					 channel_width_to_int(ci.chanwidth),
-					 ci.seg1_idx) != 0) {
-			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s",
-				ocv_errorstr);
+					 ci.seg1_idx) != OCI_SUCCESS) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+				"addr=" MACSTR " frame=eapol-key-g1 error=%s",
+				MAC2STR(sm->bssid), ocv_errorstr);
 			return -1;
 		}
 	}
@@ -2028,6 +2039,15 @@
 			os_free(rbuf);
 			return -1;
 		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (sm->oci_freq_override_eapol_g2) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI KDE frequency %d -> %d MHz",
+				   ci.frequency,
+				   sm->oci_freq_override_eapol_g2);
+			ci.frequency = sm->oci_freq_override_eapol_g2;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 		pos = key_mic + mic_len + 2; /* Key Data */
 		if (ocv_insert_oci_kde(&ci, &pos) < 0) {
@@ -2431,13 +2451,16 @@
 	u8 *tmp = NULL;
 	int ret = -1;
 	u8 *mic, *key_data;
-	size_t mic_len, keyhdrlen;
+	size_t mic_len, keyhdrlen, pmk_len;
 
 #ifdef CONFIG_IEEE80211R
 	sm->ft_completed = 0;
 #endif /* CONFIG_IEEE80211R */
 
-	mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
+	pmk_len = sm->pmk_len;
+	if (!pmk_len && sm->cur_pmksa)
+		pmk_len = sm->cur_pmksa->pmk_len;
+	mic_len = wpa_mic_len(sm->key_mgmt, pmk_len);
 	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	if (len < sizeof(*hdr) + keyhdrlen) {
@@ -3278,6 +3301,9 @@
 	case WPA_PARAM_SAE_PWE:
 		sm->sae_pwe = value;
 		break;
+	case WPA_PARAM_SAE_PK:
+		sm->sae_pk = value;
+		break;
 	case WPA_PARAM_DENY_PTK0_REKEY:
 		sm->wpa_deny_ptk0_rekey = value;
 		break;
@@ -3291,6 +3317,18 @@
 	case WPA_PARAM_FT_RSNXE_USED:
 		sm->ft_rsnxe_used = value;
 		break;
+	case WPA_PARAM_OCI_FREQ_EAPOL:
+		sm->oci_freq_override_eapol = value;
+		break;
+	case WPA_PARAM_OCI_FREQ_EAPOL_G2:
+		sm->oci_freq_override_eapol_g2 = value;
+		break;
+	case WPA_PARAM_OCI_FREQ_FT_ASSOC:
+		sm->oci_freq_override_ft_assoc = value;
+		break;
+	case WPA_PARAM_OCI_FREQ_FILS_ASSOC:
+		sm->oci_freq_override_fils_assoc = value;
+		break;
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_DPP2
 	case WPA_PARAM_DPP_PFS:
@@ -4430,10 +4468,6 @@
 		return -1;
 	}
 	sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
-	wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
-			sm->pmk_r0, sm->pmk_r0_len);
-	wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
-		    sm->pmk_r0_name, WPA_PMK_NAME_LEN);
 	wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
 		   MAC2STR(sm->r1kh_id));
 	pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
@@ -4442,8 +4476,6 @@
 		wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
 		return -1;
 	}
-	wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
-		    WPA_PMK_NAME_LEN);
 	os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 
 	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
@@ -4554,6 +4586,15 @@
 			wpabuf_free(buf);
 			return NULL;
 		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (sm->oci_freq_override_fils_assoc) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI KDE frequency %d -> %d MHz",
+				   ci.frequency,
+				   sm->oci_freq_override_fils_assoc);
+			ci.frequency = sm->oci_freq_override_fils_assoc;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 		pos = wpabuf_put(buf, OCV_OCI_EXTENDED_LEN);
 		if (ocv_insert_extended_oci(&ci, pos) < 0) {
@@ -4758,8 +4799,10 @@
 
 		if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
 					 channel_width_to_int(ci.chanwidth),
-					 ci.seg1_idx) != 0) {
-			wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr);
+					 ci.seg1_idx) != OCI_SUCCESS) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+				"addr=" MACSTR " frame=fils-assoc error=%s",
+				MAC2STR(sm->bssid), ocv_errorstr);
 			goto fail;
 		}
 	}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index dfc156b..2142772 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -43,7 +43,7 @@
 	int (*add_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
 			 const u8 *pmkid, const u8 *fils_cache_id,
 			 const u8 *pmk, size_t pmk_len, u32 pmk_lifetime,
-			 u8 pmk_reauth_threshold);
+			 u8 pmk_reauth_threshold, int akmp);
 	int (*remove_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
 			    const u8 *pmkid, const u8 *fils_cache_id);
 	void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
@@ -103,11 +103,16 @@
 	WPA_PARAM_MFP,
 	WPA_PARAM_OCV,
 	WPA_PARAM_SAE_PWE,
+	WPA_PARAM_SAE_PK,
 	WPA_PARAM_DENY_PTK0_REKEY,
 	WPA_PARAM_EXT_KEY_ID,
 	WPA_PARAM_USE_EXT_KEY_ID,
 	WPA_PARAM_FT_RSNXE_USED,
 	WPA_PARAM_DPP_PFS,
+	WPA_PARAM_OCI_FREQ_EAPOL,
+	WPA_PARAM_OCI_FREQ_EAPOL_G2,
+	WPA_PARAM_OCI_FREQ_FT_ASSOC,
+	WPA_PARAM_OCI_FREQ_FILS_ASSOC,
 };
 
 struct rsn_supp_config {
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 3e51cf2..bf73376 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -15,6 +15,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/ocv.h"
+#include "common/wpa_ctrl.h"
 #include "drivers/driver.h"
 #include "wpa.h"
 #include "wpa_i.h"
@@ -50,17 +51,11 @@
 			      sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
 			      sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0)
 		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, sm->pmk_r0_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
-		    sm->pmk_r0_name, WPA_PMK_NAME_LEN);
 	sm->pmk_r1_len = sm->pmk_r0_len;
 	if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
 			      sm->r1kh_id, sm->own_addr, sm->pmk_r1,
 			      sm->pmk_r1_name) < 0)
 		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
-		    WPA_PMK_NAME_LEN);
 	return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce,
 				 sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
 				 ptk_name, sm->key_mgmt, sm->pairwise_cipher);
@@ -358,6 +353,14 @@
 			os_free(buf);
 			return NULL;
 		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (sm->oci_freq_override_ft_assoc) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI KDE frequency %d -> %d MHz",
+				   ci.frequency, sm->oci_freq_override_ft_assoc);
+			ci.frequency = sm->oci_freq_override_ft_assoc;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 		*pos++ = FTIE_SUBELEM_OCI;
 		*pos++ = OCV_OCI_LEN;
@@ -641,9 +644,6 @@
 			      sm->pmk_r1_name) < 0)
 		return -1;
 	sm->pmk_r1_len = sm->pmk_r0_len;
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
-		    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 
 	bssid = target_ap;
 	if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
@@ -1167,8 +1167,10 @@
 
 		if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
 					 channel_width_to_int(ci.chanwidth),
-					 ci.seg1_idx) != 0) {
-			wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+					 ci.seg1_idx) != OCI_SUCCESS) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+				"addr=" MACSTR " frame=ft-assoc error=%s",
+				MAC2STR(sm->bssid), ocv_errorstr);
 			return -1;
 		}
 	}
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index f7d9f62..96b07fc 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -95,6 +95,7 @@
 	int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
 	int ocv; /* Operating Channel Validation */
 	int sae_pwe; /* SAE PWE generation options */
+	int sae_pk; /* whether SAE-PK is used */
 
 	u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
 	size_t assoc_wpa_ie_len;
@@ -154,6 +155,10 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	struct wpabuf *test_assoc_ie;
 	int ft_rsnxe_used;
+	unsigned int oci_freq_override_eapol;
+	unsigned int oci_freq_override_eapol_g2;
+	unsigned int oci_freq_override_ft_assoc;
+	unsigned int oci_freq_override_fils_assoc;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_FILS
@@ -267,12 +272,12 @@
 				   const u8 *bssid, const u8 *pmkid,
 				   const u8 *cache_id, const u8 *pmk,
 				   size_t pmk_len, u32 pmk_lifetime,
-				   u8 pmk_reauth_threshold)
+				   u8 pmk_reauth_threshold, int akmp)
 {
 	WPA_ASSERT(sm->ctx->add_pmkid);
 	return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid,
 				  cache_id, pmk, pmk_len, pmk_lifetime,
-				  pmk_reauth_threshold);
+				  pmk_reauth_threshold, akmp);
 }
 
 static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, void *network_ctx,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 9068781..20fdd69 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -357,7 +357,7 @@
 
 	if (!wpa_key_mgmt_sae(sm->key_mgmt))
 		return 0; /* SAE not in use */
-	if (sm->sae_pwe != 1 && sm->sae_pwe != 2)
+	if (sm->sae_pwe != 1 && sm->sae_pwe != 2 && !sm->sae_pk)
 		return 0; /* no supported extended RSN capabilities */
 
 	if (rsnxe_len < 3)
@@ -367,7 +367,12 @@
 	*pos++ = 1;
 	/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
 	 * used for now */
-	*pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+	*pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+	if (sm->sae_pk)
+		*pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+	pos++;
 
 	return pos - rsnxe;
 }
diff --git a/src/tls/Makefile b/src/tls/Makefile
index 52a890a..c84fbe8 100644
--- a/src/tls/Makefile
+++ b/src/tls/Makefile
@@ -1,14 +1,3 @@
-all: libtls.a
-
-clean:
-	rm -f *~ *.o *.d libtls.a
-
-install:
-	@echo Nothing to be made.
-
-
-include ../lib.rules
-
 CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
 CFLAGS += -DCONFIG_CRYPTO_INTERNAL
 CFLAGS += -DCONFIG_TLSV11
@@ -33,8 +22,4 @@
 	tlsv1_server_write.o \
 	x509v3.o
 
-
-libtls.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/utils/Makefile b/src/utils/Makefile
index 1ee2bee..e8ad997 100644
--- a/src/utils/Makefile
+++ b/src/utils/Makefile
@@ -1,14 +1,3 @@
-all: libutils.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libutils.a
-
-install:
-	@echo Nothing to be made.
-
-
-include ../lib.rules
-
 #CFLAGS += -DWPA_TRACE
 CFLAGS += -DCONFIG_IPV6
 CFLAGS += -DCONFIG_DEBUG_FILE
@@ -37,7 +26,4 @@
 
 #LIB_OBJS += pcsc_funcs.o
 
-libutils.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/utils/base64.c b/src/utils/base64.c
index a17d2d3..0d121c1 100644
--- a/src/utils/base64.c
+++ b/src/utils/base64.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 #include <stdint.h>
 
+#include "utils/common.h"
 #include "os.h"
 #include "base64.h"
 
@@ -18,6 +19,10 @@
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
 
 
+#define BASE64_PAD BIT(0)
+#define BASE64_LF BIT(1)
+
+
 static char * base64_gen_encode(const unsigned char *src, size_t len,
 				size_t *out_len, const char *table, int add_pad)
 {
@@ -29,7 +34,7 @@
 	if (len >= SIZE_MAX / 4)
 		return NULL;
 	olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
-	if (add_pad)
+	if (add_pad & BASE64_LF)
 		olen += olen / 72; /* line feeds */
 	olen++; /* nul termination */
 	if (olen < len)
@@ -49,7 +54,7 @@
 		*pos++ = table[in[2] & 0x3f];
 		in += 3;
 		line_len += 4;
-		if (add_pad && line_len >= 72) {
+		if ((add_pad & BASE64_LF) && line_len >= 72) {
 			*pos++ = '\n';
 			line_len = 0;
 		}
@@ -59,19 +64,19 @@
 		*pos++ = table[(in[0] >> 2) & 0x3f];
 		if (end - in == 1) {
 			*pos++ = table[((in[0] & 0x03) << 4) & 0x3f];
-			if (add_pad)
+			if (add_pad & BASE64_PAD)
 				*pos++ = '=';
 		} else {
 			*pos++ = table[(((in[0] & 0x03) << 4) |
 					(in[1] >> 4)) & 0x3f];
 			*pos++ = table[((in[1] & 0x0f) << 2) & 0x3f];
 		}
-		if (add_pad)
+		if (add_pad & BASE64_PAD)
 			*pos++ = '=';
 		line_len += 4;
 	}
 
-	if (add_pad && line_len)
+	if ((add_pad & BASE64_LF) && line_len)
 		*pos++ = '\n';
 
 	*pos = '\0';
@@ -164,7 +169,14 @@
  */
 char * base64_encode(const void *src, size_t len, size_t *out_len)
 {
-	return base64_gen_encode(src, len, out_len, base64_table, 1);
+	return base64_gen_encode(src, len, out_len, base64_table,
+				 BASE64_PAD | BASE64_LF);
+}
+
+
+char * base64_encode_no_lf(const void *src, size_t len, size_t *out_len)
+{
+	return base64_gen_encode(src, len, out_len, base64_table, BASE64_PAD);
 }
 
 
diff --git a/src/utils/base64.h b/src/utils/base64.h
index 6216f44..d545b29 100644
--- a/src/utils/base64.h
+++ b/src/utils/base64.h
@@ -10,6 +10,7 @@
 #define BASE64_H
 
 char * base64_encode(const void *src, size_t len, size_t *out_len);
+char * base64_encode_no_lf(const void *src, size_t len, size_t *out_len);
 unsigned char * base64_decode(const char *src, size_t len, size_t *out_len);
 char * base64_url_encode(const void *src, size_t len, size_t *out_len);
 unsigned char * base64_url_decode(const char *src, size_t len, size_t *out_len);
diff --git a/src/utils/json.c b/src/utils/json.c
index 5a0edf2..dd12f1b 100644
--- a/src/utils/json.c
+++ b/src/utils/json.c
@@ -528,6 +528,28 @@
 }
 
 
+struct wpabuf * json_get_member_base64(struct json_token *json,
+				       const char *name)
+{
+	struct json_token *token;
+	unsigned char *buf;
+	size_t buflen;
+	struct wpabuf *ret;
+
+	token = json_get_member(json, name);
+	if (!token || token->type != JSON_STRING)
+		return NULL;
+	buf = base64_decode(token->string, os_strlen(token->string), &buflen);
+	if (!buf)
+		return NULL;
+	ret = wpabuf_alloc_ext_data(buf, buflen);
+	if (!ret)
+		os_free(buf);
+
+	return ret;
+}
+
+
 static const char * json_type_str(enum json_type type)
 {
 	switch (type) {
@@ -620,6 +642,20 @@
 }
 
 
+int json_add_base64(struct wpabuf *json, const char *name, const void *val,
+		    size_t len)
+{
+	char *b64;
+
+	b64 = base64_encode_no_lf(val, len, NULL);
+	if (!b64)
+		return -1;
+	json_add_string(json, name, b64);
+	os_free(b64);
+	return 0;
+}
+
+
 void json_start_object(struct wpabuf *json, const char *name)
 {
 	if (name)
diff --git a/src/utils/json.h b/src/utils/json.h
index ca4a2e4..8448bb0 100644
--- a/src/utils/json.h
+++ b/src/utils/json.h
@@ -37,6 +37,8 @@
 struct json_token * json_get_member(struct json_token *json, const char *name);
 struct wpabuf * json_get_member_base64url(struct json_token *json,
 					  const char *name);
+struct wpabuf * json_get_member_base64(struct json_token *json,
+				       const char *name);
 void json_print_tree(struct json_token *root, char *buf, size_t buflen);
 void json_add_int(struct wpabuf *json, const char *name, int val);
 void json_add_string(struct wpabuf *json, const char *name, const char *val);
@@ -44,6 +46,8 @@
 			   const void *val, size_t len);
 int json_add_base64url(struct wpabuf *json, const char *name, const void *val,
 		       size_t len);
+int json_add_base64(struct wpabuf *json, const char *name, const void *val,
+		    size_t len);
 void json_start_object(struct wpabuf *json, const char *name);
 void json_end_object(struct wpabuf *json);
 void json_start_array(struct wpabuf *json, const char *name);
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
index 01da41b..b2a54b2 100644
--- a/src/utils/wpabuf.h
+++ b/src/utils/wpabuf.h
@@ -71,6 +71,21 @@
 }
 
 /**
+ * wpabuf_cmp - Check if two buffers contain the same data
+ * @a: wpabuf buffer
+ * @b: wpabuf buffer
+ * Returns: 0 if the two buffers contain the same data and non-zero otherwise
+ */
+static inline int wpabuf_cmp(const struct wpabuf *a, const struct wpabuf *b)
+{
+	if (!a && !b)
+		return 0;
+	if (a && b && wpabuf_size(a) == wpabuf_size(b))
+		return os_memcmp(a->buf, b->buf, wpabuf_size(a));
+	return -1;
+}
+
+/**
  * wpabuf_head - Get pointer to the head of the buffer data
  * @buf: wpabuf buffer
  * Returns: Pointer to the head of the buffer data
diff --git a/src/wps/Makefile b/src/wps/Makefile
index 4806fe8..cddc686 100644
--- a/src/wps/Makefile
+++ b/src/wps/Makefile
@@ -1,13 +1,3 @@
-all: libwps.a
-
-clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libwps.a
-
-install:
-	@echo Nothing to be made.
-
-include ../lib.rules
-
 CFLAGS += -DCONFIG_P2P
 CFLAGS += -DCONFIG_WPS_OOB
 CFLAGS += -DCONFIG_WPS_NFC
@@ -35,7 +25,4 @@
 	wps_upnp_ssdp.o \
 	wps_upnp_web.o
 
-libwps.a: $(LIB_OBJS)
-	$(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 6bded14..31d2e50 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -1298,7 +1298,7 @@
 			   "with %s", filter);
 	}
 	if (get_netif_info(er->ifname, &er->ip_addr, &er->ip_addr_text,
-			   er->mac_addr)) {
+			   NULL, er->mac_addr)) {
 		wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
 			   "for %s. Does it have IP address?", er->ifname);
 		wps_er_deinit(er, NULL, NULL);
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index 6e10e4b..ff58cb9 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -303,6 +303,14 @@
 }
 
 
+static int local_network_addr(struct upnp_wps_device_sm *sm,
+			      struct sockaddr_in *addr)
+{
+	return (addr->sin_addr.s_addr & sm->netmask.s_addr) ==
+		(sm->ip_addr & sm->netmask.s_addr);
+}
+
+
 /* subscr_addr_add_url -- add address(es) for one url to subscription */
 static void subscr_addr_add_url(struct subscription *s, const char *url,
 				size_t url_len)
@@ -320,9 +328,14 @@
 	int rerr;
 	size_t host_len, path_len;
 
-	/* url MUST begin with http: */
-	if (url_len < 7 || os_strncasecmp(url, "http://", 7))
+	/* URL MUST begin with HTTP scheme. In addition, limit the length of
+	 * the URL to 700 characters which is around the limit that was
+	 * implicitly enforced for more than 10 years due to a bug in
+	 * generating the event messages. */
+	if (url_len < 7 || os_strncasecmp(url, "http://", 7) || url_len > 700) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Reject an unacceptable URL");
 		goto fail;
+	}
 	url += 7;
 	url_len -= 7;
 
@@ -381,6 +394,7 @@
 
 	for (rp = result; rp; rp = rp->ai_next) {
 		struct subscr_addr *a;
+		struct sockaddr_in *addr = (struct sockaddr_in *) rp->ai_addr;
 
 		/* Limit no. of address to avoid denial of service attack */
 		if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
@@ -389,6 +403,13 @@
 			break;
 		}
 
+		if (!local_network_addr(s->sm, addr)) {
+			wpa_printf(MSG_INFO,
+				   "WPS UPnP: Ignore a delivery URL that points to another network %s",
+				   inet_ntoa(addr->sin_addr));
+			continue;
+		}
+
 		a = os_zalloc(sizeof(*a) + alloc_len);
 		if (a == NULL)
 			break;
@@ -841,7 +862,7 @@
 }
 
 
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
 #include <sys/sysctl.h>
 #include <net/route.h>
 #include <net/if_dl.h>
@@ -882,7 +903,7 @@
 	}
 	return 0;
 }
-#endif /* __FreeBSD__ */
+#endif /* __FreeBSD__ || __APPLE__ */
 
 
 /**
@@ -890,11 +911,12 @@
  * @net_if: Selected network interface name
  * @ip_addr: Buffer for returning IP address in network byte order
  * @ip_addr_text: Buffer for returning a pointer to allocated IP address text
+ * @netmask: Buffer for returning netmask or %NULL if not needed
  * @mac: Buffer for returning MAC address
  * Returns: 0 on success, -1 on failure
  */
 int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
-		   u8 mac[ETH_ALEN])
+		   struct in_addr *netmask, u8 mac[ETH_ALEN])
 {
 	struct ifreq req;
 	int sock = -1;
@@ -920,6 +942,19 @@
 	in_addr.s_addr = *ip_addr;
 	os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
 
+	if (netmask) {
+		os_memset(&req, 0, sizeof(req));
+		os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+		if (ioctl(sock, SIOCGIFNETMASK, &req) < 0) {
+			wpa_printf(MSG_ERROR,
+				   "WPS UPnP: SIOCGIFNETMASK failed: %d (%s)",
+				   errno, strerror(errno));
+			goto fail;
+		}
+		addr = (struct sockaddr_in *) &req.ifr_addr;
+		netmask->s_addr = addr->sin_addr.s_addr;
+	}
+
 #ifdef __linux__
 	os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
 	if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
@@ -928,7 +963,7 @@
 		goto fail;
 	}
 	os_memcpy(mac, req.ifr_addr.sa_data, 6);
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
 	if (eth_get(net_if, mac) < 0) {
 		wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
 		goto fail;
@@ -1026,11 +1061,15 @@
 
 	/* Determine which IP and mac address we're using */
 	if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
-			   sm->mac_addr)) {
+			   &sm->netmask, sm->mac_addr)) {
 		wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
 			   "for %s. Does it have IP address?", net_if);
 		goto fail;
 	}
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Local IP address %s netmask %s hwaddr "
+		   MACSTR,
+		   sm->ip_addr_text, inet_ntoa(sm->netmask),
+		   MAC2STR(sm->mac_addr));
 
 	/* Listen for incoming TCP connections so that others
 	 * can fetch our "xml files" from us.
diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c
index cca3905..b6c9478 100644
--- a/src/wps/wps_upnp_ap.c
+++ b/src/wps/wps_upnp_ap.c
@@ -76,8 +76,10 @@
 void upnp_er_remove_notification(struct wps_registrar *reg,
 				 struct subscription *s)
 {
+	bool was_sel_reg = s->selected_registrar;
+
 	s->selected_registrar = 0;
 	eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg);
-	if (reg)
+	if (reg && was_sel_reg)
 		wps_registrar_selected_registrar_changed(reg, 0);
 }
diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c
index d7e6edc..c0d9e41 100644
--- a/src/wps/wps_upnp_event.c
+++ b/src/wps/wps_upnp_event.c
@@ -147,7 +147,8 @@
 	struct wpabuf *buf;
 	char *b;
 
-	buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
+	buf = wpabuf_alloc(1000 + os_strlen(e->addr->path) +
+			   wpabuf_len(e->data));
 	if (buf == NULL)
 		return NULL;
 	wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
@@ -293,7 +294,7 @@
 
 	buf = event_build_message(e);
 	if (buf == NULL) {
-		event_retry(e, 0);
+		event_addr_failure(e);
 		return -1;
 	}
 
@@ -301,7 +302,7 @@
 					 event_http_cb, e);
 	if (e->http_event == NULL) {
 		wpabuf_free(buf);
-		event_retry(e, 0);
+		event_addr_failure(e);
 		return -1;
 	}
 
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
index e87a932..6ead7b4 100644
--- a/src/wps/wps_upnp_i.h
+++ b/src/wps/wps_upnp_i.h
@@ -128,6 +128,7 @@
 	u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
 	char *ip_addr_text; /* IP address of network i.f. we use */
 	unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+	struct in_addr netmask;
 	int multicast_sd; /* send multicast messages over this socket */
 	int ssdp_sd; /* receive discovery UPD packets on socket */
 	int ssdp_sd_registered; /* nonzero if we must unregister */
@@ -158,7 +159,7 @@
 					const u8 uuid[UUID_LEN]);
 void subscr_addr_delete(struct subscr_addr *a);
 int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
-		   u8 mac[ETH_ALEN]);
+		   struct in_addr *netmask, u8 mac[ETH_ALEN]);
 
 /* wps_upnp_ssdp.c */
 void msearchreply_state_machine_stop(struct advertisement_state_machine *a);
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 5cc9c65..3a99bb6 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -128,6 +128,7 @@
 OBJS += wmm_ac.c
 OBJS += op_classes.c
 OBJS += rrm.c
+OBJS += robust_av.c
 OBJS_p = wpa_passphrase.c
 OBJS_p += src/utils/common.c
 OBJS_p += src/utils/wpa_debug.c
@@ -265,6 +266,10 @@
 ifdef CONFIG_SAE
 L_CFLAGS += -DCONFIG_SAE
 OBJS += src/common/sae.c
+ifdef CONFIG_SAE_PK
+L_CFLAGS += -DCONFIG_SAE_PK
+OBJS += src/common/sae_pk.c
+endif
 NEED_ECC=y
 NEED_DH_GROUPS=y
 NEED_HMAC_SHA256_KDF=y
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index b35d11e..08522de 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -1,11 +1,24 @@
-ifndef CC
-CC=gcc
+BINALL=wpa_supplicant wpa_cli
+
+ifndef CONFIG_NO_WPA_PASSPHRASE
+BINALL += wpa_passphrase
 endif
 
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
+ALL = $(BINALL)
+ALL += systemd/wpa_supplicant.service
+ALL += systemd/wpa_supplicant@.service
+ALL += systemd/wpa_supplicant-nl80211@.service
+ALL += systemd/wpa_supplicant-wired@.service
+ALL += dbus/fi.w1.wpa_supplicant1.service
+ifdef CONFIG_BUILD_WPA_CLIENT_SO
+ALL += libwpa_client.so
 endif
 
+EXTRA_TARGETS=dynamic_eap_methods
+
+CONFIG_FILE=.config
+include ../src/build.rules
+
 ifdef LIBS
 # If LIBS is set with some global build system defaults, clone those for
 # LIBS_c and LIBS_p to cover wpa_passphrase and wpa_cli as well.
@@ -26,8 +39,6 @@
 CFLAGS += -I$(abspath ../src)
 CFLAGS += -I$(abspath ../src/utils)
 
--include .config
-
 ifndef CONFIG_NO_GITVER
 # Add VERSION_STR postfix for builds from a git repository
 ifeq ($(wildcard ../.git),../.git)
@@ -44,34 +55,6 @@
 CONFIG_TDLS_TESTING=y
 endif
 
-BINALL=wpa_supplicant wpa_cli
-
-ifndef CONFIG_NO_WPA_PASSPHRASE
-BINALL += wpa_passphrase
-endif
-
-ALL = $(BINALL)
-ALL += systemd/wpa_supplicant.service
-ALL += systemd/wpa_supplicant@.service
-ALL += systemd/wpa_supplicant-nl80211@.service
-ALL += systemd/wpa_supplicant-wired@.service
-ALL += dbus/fi.w1.wpa_supplicant1.service
-ifdef CONFIG_BUILD_WPA_CLIENT_SO
-ALL += libwpa_client.so
-endif
-
-
-all: verify_config $(ALL) dynamic_eap_methods
-
-verify_config:
-	@if [ ! -r .config ]; then \
-		echo 'Building wpa_supplicant requires a configuration file'; \
-		echo '(.config). See README for more instructions. You can'; \
-		echo 'run "cp defconfig .config" to create an example'; \
-		echo 'configuration.'; \
-		exit 1; \
-	fi
-
 mkconfig:
 	@if [ -f .config ]; then \
 		echo '.config exists - did not replace it'; \
@@ -107,6 +90,7 @@
 OBJS += ../src/utils/crc32.o
 OBJS += op_classes.o
 OBJS += rrm.o
+OBJS += robust_av.o
 OBJS_p = wpa_passphrase.o
 OBJS_p += ../src/utils/common.o
 OBJS_p += ../src/utils/wpa_debug.o
@@ -267,6 +251,10 @@
 ifdef CONFIG_SAE
 CFLAGS += -DCONFIG_SAE
 OBJS += ../src/common/sae.o
+ifdef CONFIG_SAE_PK
+CFLAGS += -DCONFIG_SAE_PK
+OBJS += ../src/common/sae_pk.o
+endif
 NEED_ECC=y
 NEED_DH_GROUPS=y
 NEED_HMAC_SHA256_KDF=y
@@ -1866,53 +1854,40 @@
 CFLAGS += -DCONFIG_NO_TKIP
 endif
 
-ifndef LDO
-LDO=$(CC)
-endif
-
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-ifeq ($(QUIET), 1)
-Q=@
-E=true
-endif
-
 dynamic_eap_methods: $(EAPDYN)
 
-../src/drivers/build.wpa_supplicant:
-	@if [ -f ../src/drivers/build.hostapd ]; then \
-		$(MAKE) -C ../src/drivers clean; \
-	fi
-	@touch ../src/drivers/build.wpa_supplicant
-
-BCHECK=../src/drivers/build.wpa_supplicant
-
+_OBJS_VAR := OBJS_priv
+include ../src/objs.mk
 wpa_priv: $(BCHECK) $(OBJS_priv)
 	$(Q)$(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS)
 	@$(E) "  LD " $@
 
-$(OBJS_c) $(OBJS_t) $(OBJS_t2) $(OBJS) $(BCHECK) $(EXTRA_progs): .config
-
+_OBJS_VAR := OBJS
+include ../src/objs.mk
 wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
 	$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
 	@$(E) "  LD " $@
 
+_OBJS_VAR := OBJS_t
+include ../src/objs.mk
 eapol_test: $(OBJS_t)
 	$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
 	@$(E) "  LD " $@
 
+_OBJS_VAR := OBJS_t2
+include ../src/objs.mk
 preauth_test: $(OBJS_t2)
 	$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
 	@$(E) "  LD " $@
 
+_OBJS_VAR := OBJS_p
+include ../src/objs.mk
 wpa_passphrase: $(OBJS_p)
 	$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
 	@$(E) "  LD " $@
 
+_OBJS_VAR := OBJS_c
+include ../src/objs.mk
 wpa_cli: $(OBJS_c)
 	$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
 	@$(E) "  LD " $@
@@ -1926,6 +1901,8 @@
 LIBCTRLSO += ../src/utils/common.c
 LIBCTRLSO += ../src/utils/wpa_debug.c
 
+_OBJS_VAR := LIBCTRL
+include ../src/objs.mk
 libwpa_client.a: $(LIBCTRL)
 	$(Q)rm -f $@
 	$(Q)$(AR) crs $@ $?
@@ -1935,14 +1912,19 @@
 	@$(E) "  CC  $@ ($^)"
 	$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -fPIC $^
 
-libwpa_test1: libwpa_test.o libwpa_client.a
-	$(Q)$(LDO) $(LDFLAGS) -o libwpa_test1 libwpa_test.o libwpa_client.a $(LIBS_c)
+OBJS_wpatest := libwpa_test.o
+_OBJS_VAR := OBJS_wpatest
+include ../src/objs.mk
+libwpa_test1: $(OBJS_wpatest) libwpa_client.a
+	$(Q)$(LDO) $(LDFLAGS) -o libwpa_test1 $(OBJS_wpatest) libwpa_client.a $(LIBS_c)
 	@$(E) "  LD " $@
 
-libwpa_test2: libwpa_test.o libwpa_client.so
-	$(Q)$(LDO) $(LDFLAGS) -o libwpa_test2 libwpa_test.o -L. -lwpa_client $(LIBS_c)
+libwpa_test2: $(OBJS_wpatest) libwpa_client.so
+	$(Q)$(LDO) $(LDFLAGS) -o libwpa_test2 $(OBJS_wpatest) -L. -lwpa_client $(LIBS_c)
 	@$(E) "  LD " $@
 
+_OBJS_VAR := OBJS_nfc
+include ../src/objs.mk
 nfc_pw_token: $(OBJS_nfc)
 	$(Q)$(LDO) $(LDFLAGS) -o nfc_pw_token $(OBJS_nfc) $(LIBS)
 	@$(E) "  LD " $@
@@ -1979,16 +1961,6 @@
 	$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
 		-D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
 
-ifdef CONFIG_CODE_COVERAGE
-%.o: %.c
-	@$(E) "  CC " $<
-	$(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<)
-else
-%.o: %.c
-	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
-	@$(E) "  CC " $<
-endif
-
 %.service: %.service.in
 	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
 	@$(E) "  sed" $<
@@ -2030,15 +2002,16 @@
 fips:
 	$(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)"
 
-lcov-html: wpa_supplicant.gcda
-	lcov -c -d .. > lcov.info
+.PHONY: lcov-html
+lcov-html: $(call BUILDOBJ,wpa_supplicant.gcda)
+	lcov -c -d $(BUILDDIR) > lcov.info
 	genhtml lcov.info --output-directory lcov-html
 
-clean:
+clean: common-clean
 	$(MAKE) -C ../src clean
 	$(MAKE) -C dbus clean
 	rm -f core *~ *.o *.d *.gcno *.gcda *.gcov
-	rm -f eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
+	rm -f eap_*.so $(WINALL) eapol_test preauth_test
 	rm -f wpa_priv
 	rm -f nfc_pw_token
 	rm -f lcov.info
@@ -2046,5 +2019,3 @@
 	rm -f libwpa_client.a
 	rm -f libwpa_client.so
 	rm -f libwpa_test1 libwpa_test2
-
--include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 6241682..a4892e7 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -53,30 +53,37 @@
 	u8 center_chan = 0;
 	u8 channel = conf->channel;
 #endif /* CONFIG_P2P */
+	u8 freq_seg_idx;
 
 	if (!conf->secondary_channel)
 		goto no_vht;
 
 	/* Use the maximum oper channel width if it's given. */
 	if (ssid->max_oper_chwidth)
-		conf->vht_oper_chwidth = ssid->max_oper_chwidth;
+		hostapd_set_oper_chwidth(conf, ssid->max_oper_chwidth);
 
-	ieee80211_freq_to_chan(ssid->vht_center_freq2,
-			       &conf->vht_oper_centr_freq_seg1_idx);
+	if (hostapd_get_oper_chwidth(conf) == CHANWIDTH_80P80MHZ) {
+		ieee80211_freq_to_chan(ssid->vht_center_freq2,
+				       &freq_seg_idx);
+		hostapd_set_oper_centr_freq_seg1_idx(conf, freq_seg_idx);
+	}
 
 	if (!ssid->p2p_group) {
-		if (!ssid->vht_center_freq1 ||
-		    conf->vht_oper_chwidth == CHANWIDTH_USE_HT)
+		if (!ssid->vht_center_freq1)
 			goto no_vht;
 		ieee80211_freq_to_chan(ssid->vht_center_freq1,
-				       &conf->vht_oper_centr_freq_seg0_idx);
-		wpa_printf(MSG_DEBUG, "VHT seg0 index %d for AP",
-			   conf->vht_oper_centr_freq_seg0_idx);
+				       &freq_seg_idx);
+		hostapd_set_oper_centr_freq_seg0_idx(conf, freq_seg_idx);
+
+		wpa_printf(MSG_DEBUG,
+			   "VHT seg0 index %d and seg1 index %d for AP",
+			   hostapd_get_oper_centr_freq_seg0_idx(conf),
+			   hostapd_get_oper_centr_freq_seg1_idx(conf));
 		return;
 	}
 
 #ifdef CONFIG_P2P
-	switch (conf->vht_oper_chwidth) {
+	switch (hostapd_get_oper_chwidth(conf)) {
 	case CHANWIDTH_80MHZ:
 	case CHANWIDTH_80P80MHZ:
 		center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
@@ -96,14 +103,14 @@
 		 * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is
 		 * not supported.
 		 */
-		conf->vht_oper_chwidth = CHANWIDTH_160MHZ;
+		hostapd_set_oper_chwidth(conf, CHANWIDTH_160MHZ);
 		center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
 		if (center_chan) {
 			wpa_printf(MSG_DEBUG,
 				   "VHT center channel %u for auto-selected 160 MHz bandwidth",
 				   center_chan);
 		} else {
-			conf->vht_oper_chwidth = CHANWIDTH_80MHZ;
+			hostapd_set_oper_chwidth(conf, CHANWIDTH_80MHZ);
 			center_chan = wpas_p2p_get_vht80_center(wpa_s, mode,
 								channel);
 			wpa_printf(MSG_DEBUG,
@@ -115,9 +122,9 @@
 	if (!center_chan)
 		goto no_vht;
 
-	conf->vht_oper_centr_freq_seg0_idx = center_chan;
+	hostapd_set_oper_centr_freq_seg0_idx(conf, center_chan);
 	wpa_printf(MSG_DEBUG, "VHT seg0 index %d for P2P GO",
-		   conf->vht_oper_centr_freq_seg0_idx);
+		   hostapd_get_oper_centr_freq_seg0_idx(conf));
 	return;
 #endif /* CONFIG_P2P */
 
@@ -125,9 +132,27 @@
 	wpa_printf(MSG_DEBUG,
 		   "No VHT higher bandwidth support for the selected channel %d",
 		   conf->channel);
-	conf->vht_oper_centr_freq_seg0_idx =
-		conf->channel + conf->secondary_channel * 2;
-	conf->vht_oper_chwidth = CHANWIDTH_USE_HT;
+	hostapd_set_oper_centr_freq_seg0_idx(
+		conf, conf->channel + conf->secondary_channel * 2);
+	hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT);
+}
+
+
+static struct hostapd_hw_modes *
+wpa_supplicant_find_hw_mode(struct wpa_supplicant *wpa_s,
+			    enum hostapd_hw_mode hw_mode)
+{
+	struct hostapd_hw_modes *mode = NULL;
+	int i;
+
+	for (i = 0; i < wpa_s->hw.num_modes; i++) {
+		if (wpa_s->hw.modes[i].mode == hw_mode) {
+			mode = &wpa_s->hw.modes[i];
+			break;
+		}
+	}
+
+	return mode;
 }
 
 
@@ -144,9 +169,6 @@
 		return -1;
 	}
 
-	/* TODO: enable HT40 if driver supports it;
-	 * drop to 11b if driver does not support 11g */
-
 	/*
 	 * Enable HT20 if the driver supports it, by setting conf->ieee80211n
 	 * and a mask of allowed capabilities within conf->ht_capab.
@@ -155,17 +177,28 @@
 	 */
 	if (wpa_s->hw.modes) {
 		struct hostapd_hw_modes *mode = NULL;
-		int i, no_ht = 0;
+		int no_ht = 0;
 
 		wpa_printf(MSG_DEBUG,
 			   "Determining HT/VHT options based on driver capabilities (freq=%u chan=%u)",
 			   ssid->frequency, conf->channel);
 
-		for (i = 0; i < wpa_s->hw.num_modes; i++) {
-			if (wpa_s->hw.modes[i].mode == conf->hw_mode) {
-				mode = &wpa_s->hw.modes[i];
-				break;
-			}
+		mode = wpa_supplicant_find_hw_mode(wpa_s, conf->hw_mode);
+
+		/* May drop to IEEE 802.11b if the driver does not support IEEE
+		 * 802.11g */
+		if (!mode && conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
+			conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+			wpa_printf(MSG_INFO,
+				   "Try downgrade to IEEE 802.11b as 802.11g is not supported by the current hardware");
+			mode = wpa_supplicant_find_hw_mode(wpa_s,
+							   conf->hw_mode);
+		}
+
+		if (!mode) {
+			wpa_printf(MSG_ERROR,
+				   "No match between requested and supported hw modes found");
+			return -1;
 		}
 
 #ifdef CONFIG_HT_OVERRIDES
@@ -190,6 +223,14 @@
 				      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET),
 				   ssid->ht40);
 			conf->ieee80211n = 1;
+
+			if (ssid->ht40 &&
+			    (mode->ht_capab &
+			     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+				conf->secondary_channel = ssid->ht40;
+			else
+				conf->secondary_channel = 0;
+
 #ifdef CONFIG_P2P
 			if (ssid->p2p_group &&
 			    conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
@@ -231,16 +272,19 @@
 				 HT_CAP_INFO_TX_STBC |
 				 HT_CAP_INFO_MAX_AMSDU_SIZE);
 
+			/* check this before VHT, because setting oper chan
+			 * width and friends is the same call for HE and VHT
+			 * and checks if conf->ieee8021ax == 1 */
+			if (mode->he_capab[wpas_mode_to_ieee80211_mode(
+					    ssid->mode)].he_supported &&
+			    ssid->he)
+				conf->ieee80211ax = 1;
+
 			if (mode->vht_capab && ssid->vht) {
 				conf->ieee80211ac = 1;
 				conf->vht_capab |= mode->vht_capab;
 				wpas_conf_ap_vht(wpa_s, ssid, conf, mode);
 			}
-
-			if (mode->he_capab[wpas_mode_to_ieee80211_mode(
-					    ssid->mode)].he_supported &&
-			    ssid->he)
-				conf->ieee80211ax = 1;
 		}
 	}
 
@@ -858,6 +902,9 @@
 		  wpa_s->conf->wmm_ac_params,
 		  sizeof(wpa_s->conf->wmm_ac_params));
 
+	os_memcpy(wpa_s->ap_iface->conf->tx_queue, wpa_s->conf->tx_queue,
+		  sizeof(wpa_s->conf->tx_queue));
+
 	if (params.uapsd > 0) {
 		conf->bss[0]->wmm_enabled = 1;
 		conf->bss[0]->wmm_uapsd = 1;
@@ -1415,10 +1462,17 @@
 		      struct csa_settings *settings)
 {
 #ifdef NEED_AP_MLME
-	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+	struct hostapd_iface *iface = NULL;
+
+	if (wpa_s->ap_iface)
+		iface = wpa_s->ap_iface;
+	else if (wpa_s->ifmsh)
+		iface = wpa_s->ifmsh;
+
+	if (!iface || !iface->bss[0])
 		return -1;
 
-	return hostapd_switch_channel(wpa_s->ap_iface->bss[0], settings);
+	return hostapd_switch_channel(iface->bss[0], settings);
 #else /* NEED_AP_MLME */
 	return -1;
 #endif /* NEED_AP_MLME */
diff --git a/wpa_supplicant/blacklist.c b/wpa_supplicant/blacklist.c
index e53dc38..2f32644 100644
--- a/wpa_supplicant/blacklist.c
+++ b/wpa_supplicant/blacklist.c
@@ -26,6 +26,15 @@
 	if (wpa_s == NULL || bssid == NULL)
 		return NULL;
 
+	if (wpa_s->current_ssid &&
+	    wpa_s->current_ssid->was_recently_reconfigured) {
+		wpa_blacklist_clear(wpa_s);
+		wpa_s->current_ssid->was_recently_reconfigured = false;
+		return NULL;
+	}
+
+	wpa_blacklist_update(wpa_s);
+
 	e = wpa_s->blacklist;
 	while (e) {
 		if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0)
@@ -56,16 +65,29 @@
 int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
 	struct wpa_blacklist *e;
+	struct os_reltime now;
 
 	if (wpa_s == NULL || bssid == NULL)
 		return -1;
 
 	e = wpa_blacklist_get(wpa_s, bssid);
+	os_get_reltime(&now);
 	if (e) {
+		e->blacklist_start = now;
 		e->count++;
-		wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count "
-			   "incremented to %d",
-			   MAC2STR(bssid), e->count);
+		if (e->count > 5)
+			e->timeout_secs = 1800;
+		else if (e->count == 5)
+			e->timeout_secs = 600;
+		else if (e->count == 4)
+			e->timeout_secs = 120;
+		else if (e->count == 3)
+			e->timeout_secs = 60;
+		else
+			e->timeout_secs = 10;
+		wpa_printf(MSG_INFO, "BSSID " MACSTR
+			   " blacklist count incremented to %d, blacklisting for %d seconds",
+			   MAC2STR(bssid), e->count, e->timeout_secs);
 		return e->count;
 	}
 
@@ -74,10 +96,13 @@
 		return -1;
 	os_memcpy(e->bssid, bssid, ETH_ALEN);
 	e->count = 1;
+	e->timeout_secs = 10;
+	e->blacklist_start = now;
 	e->next = wpa_s->blacklist;
 	wpa_s->blacklist = e;
-	wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist",
-		   MAC2STR(bssid));
+	wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR
+		   " into blacklist, blacklisting for %d seconds",
+		   MAC2STR(bssid), e->timeout_secs);
 
 	return e->count;
 }
@@ -117,25 +142,80 @@
 
 
 /**
+ * wpa_blacklist_is_blacklisted - Check the blacklist status of a BSS
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be checked
+ * Returns: count if BSS is currently considered to be blacklisted, 0 otherwise
+ */
+int wpa_blacklist_is_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	struct wpa_blacklist *e;
+	struct os_reltime now;
+
+	e = wpa_blacklist_get(wpa_s, bssid);
+	if (!e)
+		return 0;
+	os_get_reltime(&now);
+	if (os_reltime_expired(&now, &e->blacklist_start, e->timeout_secs))
+		return 0;
+	return e->count;
+}
+
+
+/**
  * wpa_blacklist_clear - Clear the blacklist of all entries
  * @wpa_s: Pointer to wpa_supplicant data
  */
 void wpa_blacklist_clear(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_blacklist *e, *prev;
-	int max_count = 0;
 
 	e = wpa_s->blacklist;
 	wpa_s->blacklist = NULL;
 	while (e) {
-		if (e->count > max_count)
-			max_count = e->count;
 		prev = e;
 		e = e->next;
 		wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
 			   "blacklist (clear)", MAC2STR(prev->bssid));
 		os_free(prev);
 	}
+}
 
-	wpa_s->extra_blacklist_count += max_count;
+
+/**
+ * wpa_blacklist_update - Update the entries in the blacklist,
+ * deleting entries that have been expired for over an hour.
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_blacklist_update(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_blacklist *e, *prev = NULL;
+	struct os_reltime now;
+
+	if (!wpa_s)
+		return;
+
+	e = wpa_s->blacklist;
+	os_get_reltime(&now);
+	while (e) {
+		if (os_reltime_expired(&now, &e->blacklist_start,
+				       e->timeout_secs + 3600)) {
+			struct wpa_blacklist *to_delete = e;
+
+			if (prev) {
+				prev->next = e->next;
+				e = prev->next;
+			} else {
+				wpa_s->blacklist = e->next;
+				e = wpa_s->blacklist;
+			}
+			wpa_printf(MSG_INFO, "Removed BSSID " MACSTR
+				   " from blacklist (expired)",
+				   MAC2STR(to_delete->bssid));
+			os_free(to_delete);
+		} else {
+			prev = e;
+			e = e->next;
+		}
+	}
 }
diff --git a/wpa_supplicant/blacklist.h b/wpa_supplicant/blacklist.h
index ae06986..a1c60d5 100644
--- a/wpa_supplicant/blacklist.h
+++ b/wpa_supplicant/blacklist.h
@@ -13,12 +13,21 @@
 	struct wpa_blacklist *next;
 	u8 bssid[ETH_ALEN];
 	int count;
+	/* Time of most recent blacklist event. */
+	struct os_reltime blacklist_start;
+	/*
+	 * Number of seconds after blacklist_start that the entry will be
+	 * considered blacklisted.
+	 */
+	int timeout_secs;
 };
 
 struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
 					 const u8 *bssid);
 int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid);
 int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpa_blacklist_is_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid);
 void wpa_blacklist_clear(struct wpa_supplicant *wpa_s);
+void wpa_blacklist_update(struct wpa_supplicant *wpa_s);
 
 #endif /* BLACKLIST_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index e1d9824..8a8765b 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2573,6 +2573,7 @@
 	{ STR_LEN(dpp_netaccesskey) },
 	{ INT(dpp_netaccesskey_expiry) },
 	{ STR_LEN(dpp_csign) },
+	{ STR_LEN(dpp_pp_key) },
 	{ INT_RANGE(dpp_pfs, 0, 2) },
 #endif /* CONFIG_DPP */
 	{ INT_RANGE(owe_group, 0, 65535) },
@@ -2582,6 +2583,7 @@
 	{ INT_RANGE(ft_eap_pmksa_caching, 0, 1) },
 	{ INT_RANGE(beacon_prot, 0, 1) },
 	{ INT_RANGE(transition_disable, 0, 255) },
+	{ INT_RANGE(sae_pk, 0, 2) },
 };
 
 #undef OFFSET
@@ -2775,6 +2777,7 @@
 	os_free(ssid->dpp_connector);
 	bin_clear_free(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len);
 	os_free(ssid->dpp_csign);
+	os_free(ssid->dpp_pp_key);
 	while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
 				    list))) {
 		dl_list_del(&psk->list);
@@ -3035,6 +3038,8 @@
 	ssid->wpa_deny_ptk0_rekey = PTK0_REKEY_ALLOW_ALWAYS;
 	ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
 	ssid->ht = 1;
+	ssid->vht = 1;
+	ssid->he = 1;
 #ifdef IEEE8021X_EAPOL
 	ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
 	ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
@@ -3141,6 +3146,7 @@
 		}
 		ret = -1;
 	}
+	ssid->was_recently_reconfigured = true;
 
 	return ret;
 }
@@ -4254,6 +4260,8 @@
 struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
 					   const char *driver_param)
 {
+#define ecw2cw(ecw) ((1 << (ecw)) - 1)
+
 	struct wpa_config *config;
 	const int aCWmin = 4, aCWmax = 10;
 	const struct hostapd_wmm_ac_params ac_bk =
@@ -4261,9 +4269,20 @@
 	const struct hostapd_wmm_ac_params ac_be =
 		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
 	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
-		{ aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
+		{ aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
 	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
-		{ aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
+		{ aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
+	const struct hostapd_tx_queue_params txq_bk =
+		{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+	const struct hostapd_tx_queue_params txq_be =
+		{ 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
+	const struct hostapd_tx_queue_params txq_vi =
+		{ 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
+	const struct hostapd_tx_queue_params txq_vo =
+		{ 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
+		  (ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
+
+#undef ecw2cw
 
 	config = os_zalloc(sizeof(*config));
 	if (config == NULL)
@@ -4293,6 +4312,10 @@
 	config->wmm_ac_params[1] = ac_bk;
 	config->wmm_ac_params[2] = ac_vi;
 	config->wmm_ac_params[3] = ac_vo;
+	config->tx_queue[0] = txq_vo;
+	config->tx_queue[1] = txq_vi;
+	config->tx_queue[2] = txq_be;
+	config->tx_queue[3] = txq_bk;
 	config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
 	config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
 	config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
@@ -4342,13 +4365,31 @@
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
 
+/**
+ * Structure for global configuration parsing. This data is used to implement a
+ * generic parser for the global interface configuration. The table of variables
+ * is defined below in this file (global_fields[]).
+ */
 struct global_parse_data {
+	/* Configuration variable name */
 	char *name;
+
+	/* Parser function for this variable. The parser functions return 0 or 1
+	 * to indicate success. Value 0 indicates that the parameter value may
+	 * have changed while value 1 means that the value did not change.
+	 * Error cases (failure to parse the string) are indicated by returning
+	 * -1. */
 	int (*parser)(const struct global_parse_data *data,
 		      struct wpa_config *config, int line, const char *value);
+
+	/* Getter function to print the variable in text format to buf. */
 	int (*get)(const char *name, struct wpa_config *config, long offset,
 		   char *buf, size_t buflen, int pretty_print);
+
+	/* Variable specific parameters for the parser. */
 	void *param1, *param2, *param3;
+
+	/* Indicates which configuration variable has changed. */
 	unsigned int changed_flag;
 };
 
@@ -4359,6 +4400,7 @@
 {
 	int val, *dst;
 	char *end;
+	bool same;
 
 	dst = (int *) (((u8 *) config) + (long) data->param1);
 	val = strtol(pos, &end, 0);
@@ -4367,6 +4409,7 @@
 			   line, pos);
 		return -1;
 	}
+	same = *dst == val;
 	*dst = val;
 
 	wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
@@ -4387,7 +4430,7 @@
 		return -1;
 	}
 
-	return 0;
+	return same;
 }
 
 
@@ -4395,7 +4438,7 @@
 				       struct wpa_config *config, int line,
 				       const char *pos)
 {
-	size_t len;
+	size_t len, prev_len;
 	char **dst, *tmp;
 
 	len = os_strlen(pos);
@@ -4419,11 +4462,22 @@
 		return -1;
 	}
 
+	dst = (char **) (((u8 *) config) + (long) data->param1);
+	if (*dst)
+		prev_len = os_strlen(*dst);
+	else
+		prev_len = 0;
+
+	/* No change to the previously configured value */
+	if ((!(*dst) && !pos) ||
+	    (*dst && pos && prev_len == len &&
+	     os_memcmp(*dst, pos, len) == 0))
+		return 1;
+
 	tmp = os_strdup(pos);
 	if (tmp == NULL)
 		return -1;
 
-	dst = (char **) (((u8 *) config) + (long) data->param1);
 	os_free(*dst);
 	*dst = tmp;
 	wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
@@ -4464,6 +4518,10 @@
 		return -1;
 
 	dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
+	if (wpabuf_cmp(*dst, tmp) == 0) {
+		wpabuf_free(tmp);
+		return 1;
+	}
 	wpabuf_free(*dst);
 	*dst = tmp;
 	wpa_printf(MSG_DEBUG, "%s", data->name);
@@ -4505,6 +4563,8 @@
 		return -1;
 
 	dst = (u32 *) (((u8 *) config) + (long) data->param1);
+	if (os_memcmp(dst, &addr.u.v4.s_addr, 4) == 0)
+		return 1;
 	os_memcpy(dst, &addr.u.v4.s_addr, 4);
 	wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name,
 		   WPA_GET_BE32((u8 *) dst));
@@ -4522,6 +4582,8 @@
 		wpa_printf(MSG_DEBUG, "Invalid country set");
 		return -1;
 	}
+	if (pos[0] == config->country[0] && pos[1] == config->country[1])
+		return 1;
 	config->country[0] = pos[0];
 	config->country[1] = pos[1];
 	wpa_printf(MSG_DEBUG, "country='%c%c'",
@@ -4905,7 +4967,7 @@
 	{ INT_RANGE(eapol_version, 1, 2), 0 },
 #endif /* CONFIG_MACSEC */
 	{ INT(ap_scan), 0 },
-	{ FUNC(bgscan), 0 },
+	{ FUNC(bgscan), CFG_CHANGED_BGSCAN },
 #ifdef CONFIG_MESH
 	{ INT(user_mpm), 0 },
 	{ INT_RANGE(max_peer_links, 0, 255), 0 },
@@ -4979,6 +5041,7 @@
 	{ INT(p2p_device_random_mac_addr), 0 },
 	{ FUNC(p2p_device_persistent_mac_addr), 0 },
 	{ INT(p2p_interface_random_mac_addr), 0 },
+	{ INT(p2p_6ghz_disable), 0 },
 #endif /* CONFIG_P2P */
 	{ FUNC(country), CFG_CHANGED_COUNTRY },
 	{ INT(bss_max_count), 0 },
@@ -5140,6 +5203,19 @@
 }
 
 
+/**
+ * wpa_config_process_global - Set a variable in global configuration
+ * @config: Pointer to global configuration data
+ * @pos: Name and value in the format "{name}={value}"
+ * @line: Line number in configuration file or 0 if not used
+ * Returns: 0 on success with a possible change in value, 1 on success with no
+ * change to previously configured value, or -1 on failure
+ *
+ * This function can be used to set global configuration variables based on
+ * both the configuration file and management interface input. The value
+ * parameter must be in the same format as the text-based configuration file is
+ * using. For example, strings are using double quotation marks.
+ */
 int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
 {
 	size_t i;
@@ -5152,11 +5228,14 @@
 		    pos[flen] != '=')
 			continue;
 
-		if (field->parser(field, config, line, pos + flen + 1)) {
+		ret = field->parser(field, config, line, pos + flen + 1);
+		if (ret < 0) {
 			wpa_printf(MSG_ERROR, "Line %d: failed to "
 				   "parse '%s'.", line, pos);
 			ret = -1;
 		}
+		if (ret == 1)
+			break;
 		if (field->changed_flag == CFG_CHANGED_NFC_PASSWORD_TOKEN)
 			config->wps_nfc_pw_from_config = 1;
 		config->changed_parameters |= field->changed_flag;
@@ -5164,6 +5243,26 @@
 	}
 	if (i == NUM_GLOBAL_FIELDS) {
 #ifdef CONFIG_AP
+		if (os_strncmp(pos, "tx_queue_", 9) == 0) {
+			char *tmp = os_strchr(pos, '=');
+
+			if (!tmp) {
+				if (line < 0)
+					wpa_printf(MSG_ERROR,
+						   "Line %d: invalid line %s",
+						   line, pos);
+				return -1;
+			}
+			*tmp++ = '\0';
+			if (hostapd_config_tx_queue(config->tx_queue, pos,
+						    tmp)) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: invalid TX queue item",
+					   line);
+				return -1;
+			}
+		}
+
 		if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
 			char *tmp = os_strchr(pos, '=');
 			if (tmp == NULL) {
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 0ca27cb..0b9e3db 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -376,6 +376,7 @@
 #define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
 #define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18)
 #define CFG_CHANGED_DISABLE_BTM BIT(19)
+#define CFG_CHANGED_BGSCAN BIT(20)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -779,6 +780,8 @@
 	int p2p_ignore_shared_freq;
 	int p2p_optimize_listen_chan;
 
+	int p2p_6ghz_disable;
+
 	struct wpabuf *wps_vendor_ext_m1;
 
 #define MAX_WPS_VENDOR_EXT 10
@@ -974,7 +977,7 @@
 	int go_venue_type;
 
 	/**
-	 * hessid - Homogenous ESS identifier
+	 * hessid - Homogeneous ESS identifier
 	 *
 	 * If this is set (any octet is non-zero), scans will be used to
 	 * request response only from BSSes belonging to the specified
@@ -1058,6 +1061,7 @@
 	int p2p_go_max_inactivity;
 
 	struct hostapd_wmm_ac_params wmm_ac_params[4];
+	struct hostapd_tx_queue_params tx_queue[4];
 
 	/**
 	 * auto_interworking - Whether to use network selection automatically
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 52e1372..74f91d3 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -876,9 +876,10 @@
 	write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
 	INT(disabled);
 	INT(mixed_cell);
-	INT(vht);
+	INT_DEF(vht, 1);
 	INT_DEF(ht, 1);
 	INT(ht40);
+	INT_DEF(he, 1);
 	INT_DEF(max_oper_chwidth, DEFAULT_MAX_OPER_CHWIDTH);
 	INT(vht_center_freq1);
 	INT(vht_center_freq2);
@@ -928,6 +929,7 @@
 	STR(dpp_netaccesskey);
 	INT(dpp_netaccesskey_expiry);
 	STR(dpp_csign);
+	STR(dpp_pp_key);
 	INT(dpp_pfs);
 #endif /* CONFIG_DPP */
 	INT(owe_group);
@@ -937,6 +939,7 @@
 	INT(ft_eap_pmksa_caching);
 	INT(beacon_prot);
 	INT(transition_disable);
+	INT(sae_pk);
 #ifdef CONFIG_HT_OVERRIDES
 	INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
 	INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
@@ -1332,6 +1335,10 @@
 	if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE)
 		fprintf(f, "p2p_go_freq_change_policy=%u\n",
 			config->p2p_go_freq_change_policy);
+
+	if (config->p2p_6ghz_disable)
+		fprintf(f, "p2p_6ghz_disable=%d\n", config->p2p_6ghz_disable);
+
 	if (WPA_GET_BE32(config->ip_addr_go))
 		fprintf(f, "ip_addr_go=%u.%u.%u.%u\n",
 			config->ip_addr_go[0], config->ip_addr_go[1],
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 6737223..ecf258c 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -61,6 +61,12 @@
 	WPAS_MODE_MESH = 5,
 };
 
+enum sae_pk_mode {
+	SAE_PK_MODE_AUTOMATIC = 0,
+	SAE_PK_MODE_ONLY = 1,
+	SAE_PK_MODE_DISABLED = 2,
+};
+
 /**
  * struct wpa_ssid - Network configuration data
  *
@@ -1017,6 +1023,16 @@
 	size_t dpp_csign_len;
 
 	/**
+	 * dpp_pp_key - ppKey (Configurator privacy protection public key)
+	 */
+	u8 *dpp_pp_key;
+
+	/**
+	 * dpp_pp_key_len - ppKey length in octets
+	 */
+	size_t dpp_pp_key_len;
+
+	/**
 	 * dpp_pfs - DPP PFS
 	 * 0: allow PFS to be used or not used
 	 * 1: require PFS to be used (note: not compatible with DPP R1)
@@ -1120,6 +1136,25 @@
 	 *	OWE)
 	 */
 	u8 transition_disable;
+
+	/**
+	 * sae_pk - SAE-PK mode
+	 * 0 = automatic SAE/SAE-PK selection based on password; enable
+	 * transition mode (allow SAE authentication without SAE-PK)
+	 * 1 = SAE-PK only (disable transition mode; allow SAE authentication
+	 * only with SAE-PK)
+	 * 2 = disable SAE-PK (allow SAE authentication only without SAE-PK)
+	 */
+	enum sae_pk_mode sae_pk;
+
+	/**
+	 * was_recently_reconfigured - Whether this SSID config has been changed
+	 * recently
+	 *
+	 * This is an internally used variable, i.e., not used in external
+	 * configuration.
+	 */
+	bool was_recently_reconfigured;
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index f2d4d01..f5c6587 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -751,6 +751,26 @@
 		}
 	} else if (os_strcasecmp(cmd, "ft_rsnxe_used") == 0) {
 		wpa_s->ft_rsnxe_used = atoi(value);
+	} else if (os_strcasecmp(cmd, "oci_freq_override_eapol") == 0) {
+		wpa_s->oci_freq_override_eapol = atoi(value);
+	} else if (os_strcasecmp(cmd, "oci_freq_override_saquery_req") == 0) {
+		wpa_s->oci_freq_override_saquery_req = atoi(value);
+	} else if (os_strcasecmp(cmd, "oci_freq_override_saquery_resp") == 0) {
+		wpa_s->oci_freq_override_saquery_resp = atoi(value);
+	} else if (os_strcasecmp(cmd, "oci_freq_override_eapol_g2") == 0) {
+		wpa_s->oci_freq_override_eapol_g2 = atoi(value);
+		/* Populate value to wpa_sm if already associated. */
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL_G2,
+				 wpa_s->oci_freq_override_eapol_g2);
+	} else if (os_strcasecmp(cmd, "oci_freq_override_ft_assoc") == 0) {
+		wpa_s->oci_freq_override_ft_assoc = atoi(value);
+		/* Populate value to wpa_sm if already associated. */
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FT_ASSOC,
+				 wpa_s->oci_freq_override_ft_assoc);
+	} else if (os_strcasecmp(cmd, "oci_freq_override_fils_assoc") == 0) {
+		wpa_s->oci_freq_override_fils_assoc = atoi(value);
+	} else if (os_strcasecmp(cmd, "oci_freq_override_wnm_sleep") == 0) {
+		wpa_s->oci_freq_override_wnm_sleep = atoi(value);
 	} else if (os_strcasecmp(cmd, "rsne_override_eapol") == 0) {
 		wpabuf_free(wpa_s->rsne_override_eapol);
 		if (os_strcmp(value, "NULL") == 0)
@@ -878,6 +898,8 @@
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
 		if (ret == 0)
 			wpa_supplicant_update_config(wpa_s);
+		else if (ret == 1)
+			ret = 0;
 	}
 
 	return ret;
@@ -2277,8 +2299,12 @@
 	    !wpa_s->ap_iface &&
 #endif /* CONFIG_AP */
 	    wpa_s->sme.sae.state == SAE_ACCEPTED) {
-		ret = os_snprintf(pos, end - pos, "sae_group=%d\n",
-				  wpa_s->sme.sae.group);
+		ret = os_snprintf(pos, end - pos, "sae_group=%d\n"
+				  "sae_h2e=%d\n"
+				  "sae_pk=%d\n",
+				  wpa_s->sme.sae.group,
+				  wpa_s->sme.sae.h2e,
+				  wpa_s->sme.sae.pk);
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
@@ -2934,7 +2960,7 @@
 {
 	char *pos, *end;
 	int ret;
-	const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe;
+	const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe, *rsnxe;
 
 	mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
 	p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
@@ -2961,6 +2987,21 @@
 		pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2",
 					    ie2, 2 + ie2[1]);
 	}
+	rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+	if (rsnxe && rsnxe[1] >= 1) {
+		if (rsnxe[2] & BIT(WLAN_RSNX_CAPAB_SAE_H2E)) {
+			ret = os_snprintf(pos, end - pos, "[SAE-H2E]");
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+		if (rsnxe[2] & BIT(WLAN_RSNX_CAPAB_SAE_PK)) {
+			ret = os_snprintf(pos, end - pos, "[SAE-PK]");
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+	}
 	osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
 	if (osen_ie)
 		pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
@@ -3439,38 +3480,12 @@
 	struct wpa_supplicant *wpa_s, char *cmd)
 {
 	int id;
-	struct wpa_ssid *ssid;
 	int result;
 
 	/* cmd: "<network id>" or "all" */
 	if (os_strcmp(cmd, "all") == 0) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all");
-		if (wpa_s->sched_scanning)
-			wpa_supplicant_cancel_sched_scan(wpa_s);
-
-		eapol_sm_invalidate_cached_session(wpa_s->eapol);
-		if (wpa_s->current_ssid) {
-#ifdef CONFIG_SME
-			wpa_s->sme.prev_bssid_set = 0;
-#endif /* CONFIG_SME */
-			wpa_sm_set_config(wpa_s->wpa, NULL);
-			eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
-			if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
-				wpa_s->own_disconnect_req = 1;
-			wpa_supplicant_deauthenticate(
-				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
-		}
-		ssid = wpa_s->conf->ssid;
-		while (ssid) {
-			struct wpa_ssid *remove_ssid = ssid;
-			id = ssid->id;
-			ssid = ssid->next;
-			if (wpa_s->last_ssid == remove_ssid)
-				wpa_s->last_ssid = NULL;
-			wpas_notify_network_removed(wpa_s, remove_ssid);
-			wpa_config_remove_network(wpa_s->conf, id);
-		}
-		return 0;
+		return wpa_supplicant_remove_all_networks(wpa_s);
 	}
 
 	id = atoi(cmd);
@@ -3506,6 +3521,20 @@
 	if (ret == 1)
 		return 0; /* No change to the previously configured value */
 
+#ifdef CONFIG_BGSCAN
+	if (os_strcmp(name, "bgscan") == 0) {
+		/*
+		 * Reset the bgscan parameters for the current network and
+		 * return. There's no need to flush caches for bgscan parameter
+		 * changes.
+		 */
+		if (wpa_s->current_ssid == ssid &&
+		    wpa_s->wpa_state == WPA_COMPLETED)
+			wpa_supplicant_reset_bgscan(wpa_s);
+		return 0;
+	}
+#endif /* CONFIG_BGSCAN */
+
 	if (os_strcmp(name, "bssid") != 0 &&
 	    os_strcmp(name, "bssid_hint") != 0 &&
 	    os_strcmp(name, "priority") != 0) {
@@ -4771,6 +4800,20 @@
 	}
 #endif /* CONFIG_DPP */
 
+#ifdef CONFIG_SAE
+	if (os_strcmp(field, "sae") == 0 &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
+#ifdef CONFIG_SAE_PK
+		res = os_snprintf(buf, buflen, "H2E PK");
+#else /* CONFIG_SAE_PK */
+		res = os_snprintf(buf, buflen, "H2E");
+#endif /* CONFIG_SAE_PK */
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+#endif /* CONFIG_SAE */
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
 		   field);
 
@@ -4891,7 +4934,7 @@
 	size_t i;
 	int ret;
 	char *pos, *end;
-	const u8 *ie, *ie2, *osen_ie, *mesh, *owe;
+	const u8 *ie, *ie2, *osen_ie, *mesh, *owe, *rsnxe;
 
 	pos = buf;
 	end = buf + buflen;
@@ -5011,6 +5054,21 @@
 			pos = wpa_supplicant_ie_txt(pos, end,
 						    mesh ? "RSN" : "WPA2", ie2,
 						    2 + ie2[1]);
+		rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+		if (rsnxe && rsnxe[1] >= 1) {
+			if (rsnxe[2] & BIT(WLAN_RSNX_CAPAB_SAE_H2E)) {
+				ret = os_snprintf(pos, end - pos, "[SAE-H2E]");
+				if (os_snprintf_error(end - pos, ret))
+					return -1;
+				pos += ret;
+			}
+			if (rsnxe[2] & BIT(WLAN_RSNX_CAPAB_SAE_PK)) {
+				ret = os_snprintf(pos, end - pos, "[SAE-PK]");
+				if (os_snprintf_error(end - pos, ret))
+					return -1;
+				pos += ret;
+			}
+		}
 		osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
 		if (osen_ie)
 			pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
@@ -7398,7 +7456,7 @@
 static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
 {
 	u8 dst_addr[ETH_ALEN];
-	int used;
+	int used, freq = 0;
 	char *pos;
 #define MAX_ANQP_INFO_ID 100
 	u16 id[MAX_ANQP_INFO_ID];
@@ -7412,6 +7470,15 @@
 	pos = dst + used;
 	if (*pos == ' ')
 		pos++;
+
+	if (os_strncmp(pos, "freq=", 5) == 0) {
+		freq = atoi(pos + 5);
+		pos = os_strchr(pos, ' ');
+		if (!pos)
+			return -1;
+		pos++;
+	}
+
 	while (num_id < MAX_ANQP_INFO_ID) {
 		if (os_strncmp(pos, "hs20:", 5) == 0) {
 #ifdef CONFIG_HS20
@@ -7446,7 +7513,7 @@
 	if (num_id == 0 && !subtypes && !mbo_subtypes)
 		return -1;
 
-	return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes,
+	return anqp_send_req(wpa_s, dst_addr, freq, id, num_id, subtypes,
 			     mbo_subtypes);
 }
 
@@ -8325,9 +8392,10 @@
 	wpa_s->set_sta_uapsd = 0;
 	wpa_s->sta_uapsd = 0;
 
+	wpa_s->consecutive_conn_failures = 0;
+
 	wpa_drv_radio_disable(wpa_s, 0);
 	wpa_blacklist_clear(wpa_s);
-	wpa_s->extra_blacklist_count = 0;
 	wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
 	wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
 	wpa_config_flush_blobs(wpa_s->conf);
@@ -8381,6 +8449,13 @@
 	wpabuf_free(wpa_s->rsnxe_override_eapol);
 	wpa_s->rsnxe_override_eapol = NULL;
 	wpas_clear_driver_signal_override(wpa_s);
+	wpa_s->oci_freq_override_eapol = 0;
+	wpa_s->oci_freq_override_saquery_req = 0;
+	wpa_s->oci_freq_override_saquery_resp = 0;
+	wpa_s->oci_freq_override_eapol_g2 = 0;
+	wpa_s->oci_freq_override_ft_assoc = 0;
+	wpa_s->oci_freq_override_fils_assoc = 0;
+	wpa_s->oci_freq_override_wnm_sleep = 0;
 #ifdef CONFIG_DPP
 	os_free(wpa_s->dpp_config_obj_override);
 	wpa_s->dpp_config_obj_override = NULL;
@@ -8399,6 +8474,7 @@
 	wpa_s->next_scan_bssid_wildcard_ssid = 0;
 	os_free(wpa_s->select_network_scan_freqs);
 	wpa_s->select_network_scan_freqs = NULL;
+	os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
 
 	wpa_bss_flush(wpa_s);
 	if (!dl_list_empty(&wpa_s->bss)) {
@@ -8424,6 +8500,8 @@
 	wpa_supplicant_update_channel_list(wpa_s, NULL);
 
 	free_bss_tmp_disallowed(wpa_s);
+
+	os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
 }
 
 
@@ -9264,7 +9342,7 @@
 
 	if (ip.ip_hl != 5 || ip.ip_v != 4 || ntohs(ip.ip_len) > HWSIM_IP_LEN) {
 		wpa_printf(MSG_DEBUG,
-			   "test data: RX - ignore unexpect IP header");
+			   "test data: RX - ignore unexpected IP header");
 		return;
 	}
 
@@ -10258,6 +10336,76 @@
 }
 
 
+static int wpas_ctrl_iface_configure_mscs(struct wpa_supplicant *wpa_s,
+					  const char *cmd)
+{
+	size_t frame_classifier_len;
+	const char *pos, *end;
+	struct robust_av_data *robust_av = &wpa_s->robust_av;
+	int val;
+
+	/*
+	 * format:
+	 * <add|remove|change> [up_bitmap=<hex byte>] [up_limit=<integer>]
+	 * [stream_timeout=<in TUs>] [frame_classifier=<hex bytes>]
+	 */
+	os_memset(robust_av, 0, sizeof(struct robust_av_data));
+	if (os_strncmp(cmd, "add ", 4) == 0) {
+		robust_av->request_type = SCS_REQ_ADD;
+	} else if (os_strcmp(cmd, "remove") == 0) {
+		robust_av->request_type = SCS_REQ_REMOVE;
+		robust_av->valid_config = false;
+		return wpas_send_mscs_req(wpa_s);
+	} else if (os_strncmp(cmd, "change ", 7) == 0) {
+		robust_av->request_type = SCS_REQ_CHANGE;
+	} else {
+		return -1;
+	}
+
+	pos = os_strstr(cmd, "up_bitmap=");
+	if (!pos)
+		return -1;
+
+	val = hex2byte(pos + 10);
+	if (val < 0)
+		return -1;
+	robust_av->up_bitmap = val;
+
+	pos = os_strstr(cmd, "up_limit=");
+	if (!pos)
+		return -1;
+
+	robust_av->up_limit = atoi(pos + 9);
+
+	pos = os_strstr(cmd, "stream_timeout=");
+	if (!pos)
+		return -1;
+
+	robust_av->stream_timeout = atoi(pos + 15);
+	if (robust_av->stream_timeout == 0)
+		return -1;
+
+	pos = os_strstr(cmd, "frame_classifier=");
+	if (!pos)
+		return -1;
+
+	pos += 17;
+	end = os_strchr(pos, ' ');
+	if (!end)
+		end = pos + os_strlen(pos);
+
+	frame_classifier_len = (end - pos) / 2;
+	if (frame_classifier_len > sizeof(robust_av->frame_classifier) ||
+	    hexstr2bin(pos, robust_av->frame_classifier, frame_classifier_len))
+		return -1;
+
+	robust_av->frame_classifier_len = frame_classifier_len;
+	robust_av->valid_config = true;
+
+	return wpas_send_mscs_req(wpa_s);
+}
+
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 					 char *buf, size_t *resp_len)
 {
@@ -11142,13 +11290,16 @@
 	} else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) {
 		wpas_dpp_chirp_stop(wpa_s);
 	} else if (os_strncmp(buf, "DPP_RECONFIG ", 13) == 0) {
-		struct wpa_ssid *ssid;
-
-		ssid = wpa_config_get_network(wpa_s->conf, atoi(buf + 13));
-		if (!ssid || wpas_dpp_reconfig(wpa_s, ssid) < 0)
+		if (wpas_dpp_reconfig(wpa_s, buf + 13) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DPP_CA_SET ", 11) == 0) {
+		if (wpas_dpp_ca_set(wpa_s, buf + 10) < 0)
 			reply_len = -1;
 #endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
+	} else if (os_strncmp(buf, "MSCS ", 5) == 0) {
+		if (wpas_ctrl_iface_configure_mscs(wpa_s, buf + 5))
+			reply_len = -1;
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 793a881..6dcd948 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -3212,6 +3212,14 @@
 		  END_ARGS
 	  }
 	},
+	{ "Roam", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_roam,
+	  {
+		  { "addr", "s", ARG_IN },
+		  END_ARGS
+	  }
+	},
+
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	{ "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
 	  (WPADBusMethodHandler) wpas_dbus_handler_add_blob,
@@ -3613,7 +3621,7 @@
 	},
 	{ "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_bridge_ifname,
-	  NULL,
+	  wpas_dbus_setter_bridge_ifname,
 	  NULL
 	},
 	{ "ConfigFile", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index d1f9607..86f8560 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -139,6 +139,7 @@
 	"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
 	"bssid", "scan_freq", "freq_list", "scan_ssid", "bssid_hint",
 	"bssid_blacklist", "bssid_whitelist", "group_mgmt",
+	"ignore_broadcast_ssid",
 #ifdef CONFIG_MESH
 	"mesh_basic_rates",
 #endif /* CONFIG_MESH */
@@ -229,8 +230,6 @@
 		} else if (entry.type == DBUS_TYPE_STRING) {
 			if (should_quote_opt(entry.key)) {
 				size = os_strlen(entry.str_value);
-				if (size == 0)
-					goto error;
 
 				size += 3;
 				value = os_zalloc(size);
@@ -267,8 +266,28 @@
 		} else
 			goto error;
 
-		if (wpa_config_set(ssid, entry.key, value, 0) < 0)
+		ret = wpa_config_set(ssid, entry.key, value, 0);
+		if (ret < 0)
 			goto error;
+		if (ret == 1)
+			goto skip_update;
+
+#ifdef CONFIG_BGSCAN
+		if (os_strcmp(entry.key, "bgscan") == 0) {
+			/*
+			 * Reset the bgscan parameters for the current network
+			 * and continue. There's no need to flush caches for
+			 * bgscan parameter changes.
+			 */
+			if (wpa_s->current_ssid == ssid &&
+			    wpa_s->wpa_state == WPA_COMPLETED)
+				wpa_supplicant_reset_bgscan(wpa_s);
+			os_free(value);
+			value = NULL;
+			wpa_dbus_dict_entry_clear(&entry);
+			continue;
+		}
+#endif /* CONFIG_BGSCAN */
 
 		if (os_strcmp(entry.key, "bssid") != 0 &&
 		    os_strcmp(entry.key, "priority") != 0)
@@ -290,6 +309,7 @@
 		else if (os_strcmp(entry.key, "priority") == 0)
 			wpa_config_update_prio_list(wpa_s->conf);
 
+	skip_update:
 		os_free(value);
 		value = NULL;
 		wpa_dbus_dict_entry_clear(&entry);
@@ -1797,25 +1817,6 @@
 }
 
 
-static void remove_network(void *arg, struct wpa_ssid *ssid)
-{
-	struct wpa_supplicant *wpa_s = arg;
-
-	wpas_notify_network_removed(wpa_s, ssid);
-
-	if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
-		wpa_printf(MSG_ERROR,
-			   "%s[dbus]: error occurred when removing network %d",
-			   __func__, ssid->id);
-		return;
-	}
-
-	if (ssid == wpa_s->current_ssid)
-		wpa_supplicant_deauthenticate(wpa_s,
-					      WLAN_REASON_DEAUTH_LEAVING);
-}
-
-
 /**
  * wpas_dbus_handler_remove_all_networks - Remove all configured networks
  * @message: Pointer to incoming dbus message
@@ -1827,11 +1828,8 @@
 DBusMessage * wpas_dbus_handler_remove_all_networks(
 	DBusMessage *message, struct wpa_supplicant *wpa_s)
 {
-	if (wpa_s->sched_scanning)
-		wpa_supplicant_cancel_sched_scan(wpa_s);
-
 	/* NB: could check for failure and return an error */
-	wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s);
+	wpa_supplicant_remove_all_networks(wpa_s);
 	return NULL;
 }
 
@@ -1956,6 +1954,55 @@
 }
 
 
+/**
+ * wpas_dbus_handler_roam - Initiate a roam to another BSS within the ESS
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "Roam" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_roam(DBusMessage *message,
+				     struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+	return wpas_dbus_error_unknown_error(message,
+					     "scan processing not included");
+#else /* CONFIG_NO_SCAN_PROCESSING */
+	u8 bssid[ETH_ALEN];
+	struct wpa_bss *bss;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	char *addr;
+
+	if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &addr,
+				   DBUS_TYPE_INVALID))
+		return wpas_dbus_error_invalid_args(message, NULL);
+
+	if (hwaddr_aton(addr, bssid))
+		return wpas_dbus_error_invalid_args(
+			message, "Invalid hardware address format");
+
+	wpa_printf(MSG_DEBUG, "dbus: Roam " MACSTR, MAC2STR(bssid));
+
+	if (!ssid)
+		return dbus_message_new_error(
+			message, WPAS_DBUS_ERROR_NOT_CONNECTED,
+			"This interface is not connected");
+
+	bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+	if (!bss) {
+		wpa_printf(MSG_DEBUG, "dbus: Roam: Target BSS not found");
+		return wpas_dbus_error_invalid_args(
+			message, "Target BSS not found");
+	}
+
+	wpa_s->reassociate = 1;
+	wpa_supplicant_connect(wpa_s, bss, ssid);
+
+	return NULL;
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+}
+
 #ifndef CONFIG_NO_CONFIG_BLOBS
 
 /**
@@ -3634,6 +3681,43 @@
 }
 
 
+dbus_bool_t wpas_dbus_setter_bridge_ifname(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *bridge_ifname = NULL;
+	const char *msg;
+	int r;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &bridge_ifname))
+		return FALSE;
+
+	r = wpa_supplicant_update_bridge_ifname(wpa_s, bridge_ifname);
+	if (r != 0) {
+		switch (r) {
+		case -EINVAL:
+			msg = "invalid interface name";
+			break;
+		case -EBUSY:
+			msg = "interface is busy";
+			break;
+		case -EIO:
+			msg = "socket error";
+			break;
+		default:
+			msg = "unknown error";
+			break;
+		}
+		dbus_set_error_const(error, DBUS_ERROR_FAILED, msg);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
 /**
  * wpas_dbus_getter_config_file - Get interface configuration file path
  * @iter: Pointer to incoming dbus message iter
@@ -3941,14 +4025,15 @@
 		return FALSE;
 	}
 
-	if (wpa_config_process_global(wpa_s->conf, buf, -1)) {
+	ret = wpa_config_process_global(wpa_s->conf, buf, -1);
+	if (ret < 0) {
 		dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
 			       "Failed to set interface property %s",
 			       property_desc->dbus_property);
 		return FALSE;
+	} else if (ret == 0) {
+		wpa_supplicant_update_config(wpa_s);
 	}
-
-	wpa_supplicant_update_config(wpa_s);
 	return TRUE;
 }
 
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index afa26ef..c36383f 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -117,6 +117,9 @@
 DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_roam(DBusMessage *message,
+				     struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
 					 struct wpa_supplicant *wpa_s);
 
@@ -167,6 +170,7 @@
 DECLARE_ACCESSOR(wpas_dbus_getter_ifname);
 DECLARE_ACCESSOR(wpas_dbus_getter_driver);
 DECLARE_ACCESSOR(wpas_dbus_getter_bridge_ifname);
+DECLARE_ACCESSOR(wpas_dbus_setter_bridge_ifname);
 DECLARE_ACCESSOR(wpas_dbus_getter_config_file);
 DECLARE_ACCESSOR(wpas_dbus_getter_current_bss);
 DECLARE_ACCESSOR(wpas_dbus_getter_current_network);
diff --git a/wpa_supplicant/doc/docbook/.gitignore b/wpa_supplicant/doc/docbook/.gitignore
index 8c3945c..dac35c5 100644
--- a/wpa_supplicant/doc/docbook/.gitignore
+++ b/wpa_supplicant/doc/docbook/.gitignore
@@ -1,5 +1,6 @@
 manpage.links
 manpage.refs
+manpage.log
 *.8
 *.5
 *.html
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 8c2e302..2d0c636 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -12,6 +12,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/ip_addr.h"
+#include "utils/base64.h"
 #include "common/dpp.h"
 #include "common/gas.h"
 #include "common/gas_server.h"
@@ -49,6 +50,9 @@
 #ifdef CONFIG_DPP2
 static void wpas_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
 						 void *timeout_ctx);
+static void wpas_dpp_start_gas_client(struct wpa_supplicant *wpa_s);
+static int wpas_dpp_process_conf_obj(void *ctx,
+				     struct dpp_authentication *auth);
 #endif /* CONFIG_DPP2 */
 
 static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -89,6 +93,10 @@
 				       500, wpas_dpp_tx_status, 0);
 	}
 
+#ifdef CONFIG_DPP2
+	dpp_controller_new_qr_code(wpa_s->dpp, bi);
+#endif /* CONFIG_DPP2 */
+
 	return bi->id;
 }
 
@@ -832,7 +840,9 @@
 
 #ifdef CONFIG_DPP2
 	if (tcp)
-		return dpp_tcp_init(wpa_s->dpp, auth, &ipaddr, tcp_port);
+		return dpp_tcp_init(wpa_s->dpp, auth, &ipaddr, tcp_port,
+				    wpa_s->conf->dpp_name, DPP_NETROLE_STA,
+				    wpa_s, wpa_s, wpas_dpp_process_conf_obj);
 #endif /* CONFIG_DPP2 */
 
 	wpa_s->dpp_auth = auth;
@@ -1185,6 +1195,15 @@
 		ssid->dpp_csign_len = wpabuf_len(conf->c_sign_key);
 	}
 
+	if (conf->pp_key) {
+		ssid->dpp_pp_key = os_malloc(wpabuf_len(conf->pp_key));
+		if (!ssid->dpp_pp_key)
+			goto fail;
+		os_memcpy(ssid->dpp_pp_key, wpabuf_head(conf->pp_key),
+			  wpabuf_len(conf->pp_key));
+		ssid->dpp_pp_key_len = wpabuf_len(conf->pp_key);
+	}
+
 	if (auth->net_access_key) {
 		ssid->dpp_netaccesskey =
 			os_malloc(wpabuf_len(auth->net_access_key));
@@ -1220,6 +1239,102 @@
 		}
 	}
 
+#if defined(CONFIG_DPP2) && defined(IEEE8021X_EAPOL)
+	if (conf->akm == DPP_AKM_DOT1X) {
+		int i;
+		char name[100], blobname[128];
+		struct wpa_config_blob *blob;
+
+		ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X |
+			WPA_KEY_MGMT_IEEE8021X_SHA256 |
+			WPA_KEY_MGMT_IEEE8021X_SHA256;
+		ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+
+		if (conf->cacert) {
+			/* caCert is DER-encoded X.509v3 certificate for the
+			 * server certificate if that is different from the
+			 * trust root included in certBag. */
+			/* TODO: ssid->eap.cert.ca_cert */
+		}
+
+		if (conf->certs) {
+			for (i = 0; ; i++) {
+				os_snprintf(name, sizeof(name), "dpp-certs-%d",
+					    i);
+				if (!wpa_config_get_blob(wpa_s->conf, name))
+					break;
+			}
+
+			blob = os_zalloc(sizeof(*blob));
+			if (!blob)
+				goto fail;
+			blob->len = wpabuf_len(conf->certs);
+			blob->name = os_strdup(name);
+			blob->data = os_malloc(blob->len);
+			if (!blob->name || !blob->data) {
+				wpa_config_free_blob(blob);
+				goto fail;
+			}
+			os_memcpy(blob->data, wpabuf_head(conf->certs),
+				  blob->len);
+			os_snprintf(blobname, sizeof(blobname), "blob://%s",
+				    name);
+			wpa_config_set_blob(wpa_s->conf, blob);
+			wpa_printf(MSG_DEBUG, "DPP: Added certificate blob %s",
+				   name);
+			ssid->eap.cert.client_cert = os_strdup(blobname);
+			if (!ssid->eap.cert.client_cert)
+				goto fail;
+
+			/* TODO: ssid->eap.identity from own certificate */
+			if (wpa_config_set(ssid, "identity", "\"dpp-ent\"",
+					   0) < 0)
+				goto fail;
+		}
+
+		if (auth->priv_key) {
+			for (i = 0; ; i++) {
+				os_snprintf(name, sizeof(name), "dpp-key-%d",
+					    i);
+				if (!wpa_config_get_blob(wpa_s->conf, name))
+					break;
+			}
+
+			blob = os_zalloc(sizeof(*blob));
+			if (!blob)
+				goto fail;
+			blob->len = wpabuf_len(auth->priv_key);
+			blob->name = os_strdup(name);
+			blob->data = os_malloc(blob->len);
+			if (!blob->name || !blob->data) {
+				wpa_config_free_blob(blob);
+				goto fail;
+			}
+			os_memcpy(blob->data, wpabuf_head(auth->priv_key),
+				  blob->len);
+			os_snprintf(blobname, sizeof(blobname), "blob://%s",
+				    name);
+			wpa_config_set_blob(wpa_s->conf, blob);
+			wpa_printf(MSG_DEBUG, "DPP: Added private key blob %s",
+				   name);
+			ssid->eap.cert.private_key = os_strdup(blobname);
+			if (!ssid->eap.cert.private_key)
+				goto fail;
+		}
+
+		if (conf->server_name) {
+			ssid->eap.cert.domain_suffix_match =
+				os_strdup(conf->server_name);
+			if (!ssid->eap.cert.domain_suffix_match)
+				goto fail;
+		}
+
+		/* TODO: Use entCreds::eapMethods */
+		if (wpa_config_set(ssid, "eap", "TLS", 0) < 0)
+			goto fail;
+	}
+#endif /* CONFIG_DPP2 && IEEE8021X_EAPOL */
+
 	os_memcpy(wpa_s->dpp_last_ssid, conf->ssid, conf->ssid_len);
 	wpa_s->dpp_last_ssid_len = conf->ssid_len;
 
@@ -1329,6 +1444,20 @@
 			os_free(hex);
 		}
 	}
+	if (conf->pp_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(conf->pp_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(conf->pp_key),
+					 wpabuf_len(conf->pp_key));
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PP_KEY "%s", hex);
+			os_free(hex);
+		}
+	}
 	if (auth->net_access_key) {
 		char *hex;
 		size_t hexlen;
@@ -1351,6 +1480,32 @@
 		}
 	}
 
+#ifdef CONFIG_DPP2
+	if (conf->certbag) {
+		char *b64;
+
+		b64 = base64_encode_no_lf(wpabuf_head(conf->certbag),
+					  wpabuf_len(conf->certbag), NULL);
+		if (b64)
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CERTBAG "%s", b64);
+		os_free(b64);
+	}
+
+	if (conf->cacert) {
+		char *b64;
+
+		b64 = base64_encode_no_lf(wpabuf_head(conf->cacert),
+					  wpabuf_len(conf->cacert), NULL);
+		if (b64)
+			wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CACERT "%s", b64);
+		os_free(b64);
+	}
+
+	if (conf->server_name)
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_SERVER_NAME "%s",
+			conf->server_name);
+#endif /* CONFIG_DPP2 */
+
 	return wpas_dpp_process_config(wpa_s, auth, conf);
 }
 
@@ -1366,6 +1521,7 @@
 
 	wpa_printf(MSG_DEBUG, "DPP: Received Configurator backup");
 	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+	wpa_s->dpp_conf_backup_received = true;
 
 	while (key) {
 		res = dpp_configurator_from_backup(wpa_s->dpp, key);
@@ -1381,6 +1537,31 @@
 }
 
 
+#ifdef CONFIG_DPP2
+static void wpas_dpp_build_csr(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+	if (!auth || !auth->csrattrs)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build CSR");
+	wpabuf_free(auth->csr);
+	/* TODO: Additional information needed for CSR based on csrAttrs */
+	auth->csr = dpp_build_csr(auth, wpa_s->conf->dpp_name ?
+				  wpa_s->conf->dpp_name : "Test");
+	if (!auth->csr) {
+		dpp_auth_deinit(wpa_s->dpp_auth);
+		wpa_s->dpp_auth = NULL;
+		return;
+	}
+
+	wpas_dpp_start_gas_client(wpa_s);
+}
+#endif /* CONFIG_DPP2 */
+
+
 static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
 				 enum gas_query_result result,
 				 const struct wpabuf *adv_proto,
@@ -1425,11 +1606,20 @@
 		goto fail;
 	}
 
-	if (dpp_conf_resp_rx(auth, resp) < 0) {
+	res = dpp_conf_resp_rx(auth, resp);
+#ifdef CONFIG_DPP2
+	if (res == -2) {
+		wpa_printf(MSG_DEBUG, "DPP: CSR needed");
+		eloop_register_timeout(0, 0, wpas_dpp_build_csr, wpa_s, NULL);
+		return;
+	}
+#endif /* CONFIG_DPP2 */
+	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
 		goto fail;
 	}
 
+	wpa_s->dpp_conf_backup_received = false;
 	for (i = 0; i < auth->num_conf_obj; i++) {
 		res = wpas_dpp_handle_config_obj(wpa_s, auth,
 						 &auth->conf_obj[i]);
@@ -1475,6 +1665,9 @@
 		wpabuf_free(msg);
 
 		/* This exchange will be terminated in the TX status handler */
+		if (wpa_s->conf->dpp_config_processing < 2 ||
+		    wpa_s->dpp_conf_backup_received)
+			auth->remove_on_tx_status = 1;
 		return;
 	}
 fail2:
@@ -1828,6 +2021,8 @@
 	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
 		    r_bootstrap, r_bootstrap_len);
 	peer_bi = dpp_bootstrap_find_chirp(wpa_s->dpp, r_bootstrap);
+	dpp_notify_chirp_received(wpa_s, peer_bi ? (int) peer_bi->id : -1, src,
+				  freq, r_bootstrap);
 	if (!peer_bi) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No matching bootstrapping information found");
@@ -1879,11 +2074,12 @@
 				  const u8 *hdr, const u8 *buf, size_t len,
 				  unsigned int freq)
 {
-	const u8 *csign_hash;
-	u16 csign_hash_len;
+	const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
+	u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
 	struct dpp_configurator *conf;
 	struct dpp_authentication *auth;
 	unsigned int wait_time, max_wait_time;
+	u16 group;
 
 	if (!wpa_s->dpp)
 		return;
@@ -1913,7 +2109,21 @@
 		return;
 	}
 
-	auth = dpp_reconfig_init(wpa_s->dpp, wpa_s, conf, freq);
+	fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+			       &fcgroup_len);
+	if (!fcgroup || fcgroup_len != 2) {
+		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Finite Cyclic Group attribute");
+		return;
+	}
+	group = WPA_GET_LE16(fcgroup);
+	wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
+
+	a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
+	e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
+
+	auth = dpp_reconfig_init(wpa_s->dpp, wpa_s, conf, freq, group,
+				 a_nonce, a_nonce_len, e_id, e_id_len);
 	if (!auth)
 		return;
 	wpas_dpp_set_testing_options(wpa_s, auth);
@@ -1963,17 +2173,29 @@
 	wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Request from "
 		   MACSTR, MAC2STR(src));
 
-	if (!wpa_s->dpp || wpa_s->dpp_auth ||
-	    !wpa_s->dpp_reconfig_announcement || !wpa_s->dpp_reconfig_ssid)
+	if (!wpa_s->dpp)
 		return;
+	if (wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not ready for reconfiguration - pending authentication exchange in progress");
+		return;
+	}
+	if (!wpa_s->dpp_reconfig_ssid) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not ready for reconfiguration - not requested");
+		return;
+	}
 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 		if (ssid == wpa_s->dpp_reconfig_ssid &&
 		    ssid->id == wpa_s->dpp_reconfig_ssid_id)
 			break;
 	}
 	if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
-	    !ssid->dpp_csign)
+	    !ssid->dpp_csign) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not ready for reconfiguration - no matching network profile with Connector found");
 		return;
+	}
 
 	auth = dpp_reconfig_auth_req_rx(wpa_s->dpp, wpa_s, ssid->dpp_connector,
 					ssid->dpp_netaccesskey,
@@ -2667,8 +2889,8 @@
 
 
 static struct wpabuf *
-wpas_dpp_gas_req_handler(void *ctx, const u8 *sa, const u8 *query,
-			 size_t query_len)
+wpas_dpp_gas_req_handler(void *ctx, void *resp_ctx, const u8 *sa,
+			 const u8 *query, size_t query_len, u16 *comeback_delay)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct dpp_authentication *auth = wpa_s->dpp_auth;
@@ -2699,6 +2921,16 @@
 	wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR,
 		MAC2STR(sa));
 	resp = dpp_conf_req_rx(auth, query, query_len);
+
+#ifdef CONFIG_DPP2
+	if (!resp && auth->waiting_cert) {
+		wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
+		auth->cert_resp_ctx = resp_ctx;
+		*comeback_delay = 500;
+		return NULL;
+	}
+#endif /* CONFIG_DPP2 */
+
 	if (!resp) {
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
 		wpas_notify_dpp_configuration_failure(wpa_s);
@@ -2726,6 +2958,14 @@
 		return;
 	}
 
+#ifdef CONFIG_DPP2
+	if (auth->waiting_csr && ok) {
+		wpa_printf(MSG_DEBUG, "DPP: Waiting for CSR");
+		wpabuf_free(resp);
+		return;
+	}
+#endif /* CONFIG_DPP2 */
+
 	wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
 		   ok);
 	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
@@ -3087,10 +3327,8 @@
 		return -1;
 
 	os_memset(&config, 0, sizeof(config));
-	config.msg_ctx = wpa_s;
 	config.cb_ctx = wpa_s;
 #ifdef CONFIG_DPP2
-	config.process_conf_obj = wpas_dpp_process_conf_obj;
 	config.remove_bi = wpas_dpp_remove_bi;
 #endif /* CONFIG_DPP2 */
 	wpa_s->dpp = dpp_global_init(&config);
@@ -3111,7 +3349,6 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 	if (!wpa_s->dpp)
 		return;
-	dpp_global_clear(wpa_s->dpp);
 	eloop_cancel_timeout(wpas_dpp_pkex_retry_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
@@ -3123,9 +3360,12 @@
 	eloop_cancel_timeout(wpas_dpp_conn_status_result_timeout, wpa_s, NULL);
 	eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout,
 			     wpa_s, NULL);
+	eloop_cancel_timeout(wpas_dpp_build_csr, wpa_s, NULL);
 	dpp_pfs_free(wpa_s->dpp_pfs);
 	wpa_s->dpp_pfs = NULL;
 	wpas_dpp_chirp_stop(wpa_s);
+	dpp_free_reconfig_id(wpa_s->dpp_reconfig_id);
+	wpa_s->dpp_reconfig_id = NULL;
 #endif /* CONFIG_DPP2 */
 	offchannel_send_action_done(wpa_s);
 	wpas_dpp_listen_stop(wpa_s);
@@ -3134,6 +3374,7 @@
 	os_memset(wpa_s->dpp_intro_bssid, 0, ETH_ALEN);
 	os_free(wpa_s->dpp_configurator_params);
 	wpa_s->dpp_configurator_params = NULL;
+	dpp_global_clear(wpa_s->dpp);
 }
 
 
@@ -3146,6 +3387,10 @@
 
 	os_memset(&config, 0, sizeof(config));
 	config.allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
+	config.netrole = DPP_NETROLE_STA;
+	config.msg_ctx = wpa_s;
+	config.cb_ctx = wpa_s;
+	config.process_conf_obj = wpas_dpp_process_conf_obj;
 	if (cmd) {
 		pos = os_strstr(cmd, " tcp_port=");
 		if (pos) {
@@ -3166,6 +3411,8 @@
 			else
 				return -1;
 		}
+
+		config.qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
 	}
 	config.configurator_params = wpa_s->dpp_configurator_params;
 	return dpp_controller_start(wpa_s->dpp, &config);
@@ -3208,13 +3455,26 @@
 
 static void wpas_dpp_chirp_start(struct wpa_supplicant *wpa_s)
 {
-	struct wpabuf *msg;
+	struct wpabuf *msg, *announce = NULL;
 	int type;
 
 	msg = wpa_s->dpp_presence_announcement;
 	type = DPP_PA_PRESENCE_ANNOUNCEMENT;
 	if (!msg) {
-		msg = wpa_s->dpp_reconfig_announcement;
+		struct wpa_ssid *ssid = wpa_s->dpp_reconfig_ssid;
+
+		if (ssid && wpa_s->dpp_reconfig_id &&
+		    wpa_config_get_network(wpa_s->conf,
+					   wpa_s->dpp_reconfig_ssid_id) ==
+		    ssid) {
+			announce = dpp_build_reconfig_announcement(
+				ssid->dpp_csign,
+				ssid->dpp_csign_len,
+				ssid->dpp_netaccesskey,
+				ssid->dpp_netaccesskey_len,
+				wpa_s->dpp_reconfig_id);
+			msg = announce;
+		}
 		if (!msg)
 			return;
 		type = DPP_PA_RECONFIG_ANNOUNCEMENT;
@@ -3228,6 +3488,8 @@
 		    wpabuf_head(msg), wpabuf_len(msg),
 		    2000, wpas_dpp_chirp_tx_status, 0) < 0)
 		wpas_dpp_chirp_stop(wpa_s);
+
+	wpabuf_free(announce);
 }
 
 
@@ -3239,8 +3501,9 @@
 	struct hostapd_hw_modes *mode;
 	int c;
 	struct wpa_bss *bss;
+	bool chan6;
 
-	if (!bi && !wpa_s->dpp_reconfig_announcement)
+	if (!bi && !wpa_s->dpp_reconfig_ssid)
 		return;
 
 	wpa_s->dpp_chirp_scan_done = 1;
@@ -3256,7 +3519,22 @@
 	}
 
 	/* Preferred chirping channels */
-	int_array_add_unique(&wpa_s->dpp_chirp_freqs, 2437);
+	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+			HOSTAPD_MODE_IEEE80211G, 0);
+	chan6 = mode == NULL;
+	if (mode) {
+		for (c = 0; c < mode->num_channels; c++) {
+			struct hostapd_channel_data *chan = &mode->channels[c];
+
+			if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
+			    chan->freq != 2437)
+				continue;
+			chan6 = true;
+			break;
+		}
+	}
+	if (chan6)
+		int_array_add_unique(&wpa_s->dpp_chirp_freqs, 2437);
 
 	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
 			HOSTAPD_MODE_IEEE80211A, 0);
@@ -3428,15 +3706,13 @@
 void wpas_dpp_chirp_stop(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->dpp_presence_announcement ||
-	    wpa_s->dpp_reconfig_announcement) {
+	    wpa_s->dpp_reconfig_ssid) {
 		offchannel_send_action_done(wpa_s);
 		wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CHIRP_STOPPED);
 	}
 	wpa_s->dpp_chirp_bi = NULL;
 	wpabuf_free(wpa_s->dpp_presence_announcement);
 	wpa_s->dpp_presence_announcement = NULL;
-	wpabuf_free(wpa_s->dpp_reconfig_announcement);
-	wpa_s->dpp_reconfig_announcement = NULL;
 	if (wpa_s->dpp_chirp_listen)
 		wpas_dpp_listen_stop(wpa_s);
 	wpa_s->dpp_chirp_listen = 0;
@@ -3452,23 +3728,55 @@
 }
 
 
-int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, const char *cmd)
 {
-	if (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
-	    !ssid->dpp_csign)
-		return -1;
+	struct wpa_ssid *ssid;
+	int iter = 1;
+	const char *pos;
 
+	ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
+	if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+	    !ssid->dpp_csign) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not a valid network profile for reconfiguration");
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " iter=");
+	if (pos) {
+		iter = atoi(pos + 6);
+		if (iter <= 0)
+			return -1;
+	}
+
+	if (wpa_s->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not ready to start reconfiguration - pending authentication exchange in progress");
+		return -1;
+	}
+
+	dpp_free_reconfig_id(wpa_s->dpp_reconfig_id);
+	wpa_s->dpp_reconfig_id = dpp_gen_reconfig_id(ssid->dpp_csign,
+						     ssid->dpp_csign_len,
+						     ssid->dpp_pp_key,
+						     ssid->dpp_pp_key_len);
+	if (!wpa_s->dpp_reconfig_id) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to generate E-id for reconfiguration");
+		return -1;
+	}
+	if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+		wpa_printf(MSG_DEBUG, "DPP: Disconnect for reconfiguration");
+		wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(
+			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+	}
 	wpas_dpp_chirp_stop(wpa_s);
 	wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
 	wpa_s->dpp_qr_mutual = 0;
-	wpa_s->dpp_reconfig_announcement =
-		dpp_build_reconfig_announcement(ssid->dpp_csign,
-						ssid->dpp_csign_len);
-	if (!wpa_s->dpp_reconfig_announcement)
-		return -1;
 	wpa_s->dpp_reconfig_ssid = ssid;
 	wpa_s->dpp_reconfig_ssid_id = ssid->id;
-	wpa_s->dpp_chirp_iter = 1;
+	wpa_s->dpp_chirp_iter = iter;
 	wpa_s->dpp_chirp_round = 0;
 	wpa_s->dpp_chirp_scan_done = 0;
 	wpa_s->dpp_chirp_listen = 0;
@@ -3476,4 +3784,111 @@
 	return eloop_register_timeout(0, 0, wpas_dpp_chirp_next, wpa_s, NULL);
 }
 
+
+static int wpas_dpp_build_conf_resp(struct wpa_supplicant *wpa_s,
+				    struct dpp_authentication *auth, bool tcp)
+{
+	struct wpabuf *resp;
+
+	resp = dpp_build_conf_resp(auth, auth->e_nonce, auth->curve->nonce_len,
+				   auth->e_netrole, true);
+	if (!resp)
+		return -1;
+
+	if (tcp) {
+		auth->conf_resp_tcp = resp;
+		return 0;
+	}
+
+	if (gas_server_set_resp(wpa_s->gas_server, auth->cert_resp_ctx,
+				resp) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not find pending GAS response");
+		wpabuf_free(resp);
+		return -1;
+	}
+	auth->conf_resp = resp;
+	return 0;
+}
+
+
+int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	int peer = -1;
+	const char *pos, *value;
+	struct dpp_authentication *auth = wpa_s->dpp_auth;
+	u8 *bin;
+	size_t bin_len;
+	struct wpabuf *buf;
+	bool tcp = false;
+
+	pos = os_strstr(cmd, " peer=");
+	if (pos) {
+		peer = atoi(pos + 6);
+		if (!auth || !auth->waiting_cert ||
+		    (auth->peer_bi &&
+		     (unsigned int) peer != auth->peer_bi->id)) {
+			auth = dpp_controller_get_auth(wpa_s->dpp, peer);
+			tcp = true;
+		}
+	}
+
+	if (!auth || !auth->waiting_cert) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No authentication exchange waiting for certificate information");
+		return -1;
+	}
+
+	if (peer >= 0 &&
+	    (!auth->peer_bi ||
+	     (unsigned int) peer != auth->peer_bi->id) &&
+	    (!auth->tmp_peer_bi ||
+	     (unsigned int) peer != auth->tmp_peer_bi->id)) {
+		wpa_printf(MSG_DEBUG, "DPP: Peer mismatch");
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " value=");
+	if (!pos)
+		return -1;
+	value = pos + 7;
+
+	pos = os_strstr(cmd, " name=");
+	if (!pos)
+		return -1;
+	pos += 6;
+
+	if (os_strncmp(pos, "status ", 7) == 0) {
+		auth->force_conf_resp_status = atoi(value);
+		return wpas_dpp_build_conf_resp(wpa_s, auth, tcp);
+	}
+
+	if (os_strncmp(pos, "trustedEapServerName ", 21) == 0) {
+		os_free(auth->trusted_eap_server_name);
+		auth->trusted_eap_server_name = os_strdup(value);
+		return auth->trusted_eap_server_name ? 0 : -1;
+	}
+
+	bin = base64_decode(value, os_strlen(value), &bin_len);
+	if (!bin)
+		return -1;
+	buf = wpabuf_alloc_copy(bin, bin_len);
+	os_free(bin);
+
+	if (os_strncmp(pos, "caCert ", 7) == 0) {
+		wpabuf_free(auth->cacert);
+		auth->cacert = buf;
+		return 0;
+	}
+
+	if (os_strncmp(pos, "certBag ", 8) == 0) {
+		wpabuf_free(auth->certbag);
+		auth->certbag = buf;
+		return wpas_dpp_build_conf_resp(wpa_s, auth, tcp);
+	}
+
+	wpabuf_free(buf);
+	return -1;
+}
+
 #endif /* CONFIG_DPP2 */
diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h
index 2dc86e0..b0d5fcf 100644
--- a/wpa_supplicant/dpp_supplicant.h
+++ b/wpa_supplicant/dpp_supplicant.h
@@ -39,6 +39,7 @@
 				      enum dpp_status_error result);
 int wpas_dpp_chirp(struct wpa_supplicant *wpa_s, const char *cmd);
 void wpas_dpp_chirp_stop(struct wpa_supplicant *wpa_s);
-int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd);
 
 #endif /* DPP_SUPPLICANT_H */
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 54ae03b..ba8cc55 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -1069,14 +1069,14 @@
 	return wpa_s->driver->ignore_assoc_disallow(wpa_s->drv_priv, val);
 }
 
-static inline int wpa_drv_set_bssid_blacklist(struct wpa_supplicant *wpa_s,
-					      unsigned int num_bssid,
-					      const u8 *bssids)
+static inline int wpa_drv_set_bssid_tmp_disallow(struct wpa_supplicant *wpa_s,
+						 unsigned int num_bssid,
+						 const u8 *bssids)
 {
-	if (!wpa_s->driver->set_bssid_blacklist)
+	if (!wpa_s->driver->set_bssid_tmp_disallow)
 		return -1;
-	return wpa_s->driver->set_bssid_blacklist(wpa_s->drv_priv, num_bssid,
-						  bssids);
+	return wpa_s->driver->set_bssid_tmp_disallow(wpa_s->drv_priv, num_bssid,
+						     bssids);
 }
 
 static inline int wpa_drv_update_connect_params(
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 6b33ee3..fd4511d 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -356,6 +356,9 @@
 	int pmksa_set = -1;
 	size_t i;
 
+	/* Start with assumption of no PMKSA cache entry match */
+	pmksa_cache_clear_current(wpa_s->wpa);
+
 	if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 ||
 	    ie.pmkid == NULL)
 		return;
@@ -1080,32 +1083,407 @@
 }
 
 
+static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+			    const u8 *match_ssid, size_t match_ssid_len,
+			    struct wpa_bss *bss, int blacklist_count,
+			    bool debug_print);
+
+
+#ifdef CONFIG_SAE_PK
+static bool sae_pk_acceptable_bss_with_pk(struct wpa_supplicant *wpa_s,
+					  struct wpa_bss *orig_bss,
+					  struct wpa_ssid *ssid,
+					  const u8 *match_ssid,
+					  size_t match_ssid_len)
+{
+	struct wpa_bss *bss;
+
+	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+		int count;
+		const u8 *ie;
+		u8 rsnxe_capa = 0;
+
+		if (bss == orig_bss)
+			continue;
+		ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+		if (ie && ie[1] >= 1)
+			rsnxe_capa = ie[2];
+		if (!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)))
+			continue;
+
+		/* TODO: Could be more thorough in checking what kind of
+		 * signal strength or throughput estimate would be acceptable
+		 * compared to the originally selected BSS. */
+		if (bss->est_throughput < 2000)
+			return false;
+
+		count = wpa_blacklist_is_blacklisted(wpa_s, bss->bssid);
+		if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
+				    bss, count, 0))
+			return true;
+	}
+
+	return false;
+}
+#endif /* CONFIG_SAE_PK */
+
+
+static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+			    const u8 *match_ssid, size_t match_ssid_len,
+			    struct wpa_bss *bss, int blacklist_count,
+			    bool debug_print)
+{
+	int res;
+	bool wpa, check_ssid, osen, rsn_osen = false;
+	struct wpa_ie_data data;
+#ifdef CONFIG_MBO
+	const u8 *assoc_disallow;
+#endif /* CONFIG_MBO */
+#ifdef CONFIG_SAE
+	u8 rsnxe_capa = 0;
+#endif /* CONFIG_SAE */
+	const u8 *ie;
+
+	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+	wpa = ie && ie[1];
+	ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	wpa |= ie && ie[1];
+	if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 &&
+	    (data.key_mgmt & WPA_KEY_MGMT_OSEN))
+		rsn_osen = true;
+	ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+	osen = ie != NULL;
+
+#ifdef CONFIG_SAE
+	ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+	if (ie && ie[1] >= 1)
+		rsnxe_capa = ie[2];
+#endif /* CONFIG_SAE */
+
+	check_ssid = wpa || ssid->ssid_len > 0;
+
+	if (wpas_network_disabled(wpa_s, ssid)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled");
+		return false;
+	}
+
+	res = wpas_temp_disabled(wpa_s, ssid);
+	if (res > 0) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - disabled temporarily for %d second(s)",
+				res);
+		return false;
+	}
+
+#ifdef CONFIG_WPS
+	if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && blacklist_count) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - blacklisted (WPS)");
+		return false;
+	}
+
+	if (wpa && ssid->ssid_len == 0 &&
+	    wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+		check_ssid = false;
+
+	if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+		/* Only allow wildcard SSID match if an AP advertises active
+		 * WPS operation that matches our mode. */
+		check_ssid = ssid->ssid_len > 0 ||
+			!wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss);
+	}
+#endif /* CONFIG_WPS */
+
+	if (ssid->bssid_set && ssid->ssid_len == 0 &&
+	    os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
+		check_ssid = false;
+
+	if (check_ssid &&
+	    (match_ssid_len != ssid->ssid_len ||
+	     os_memcmp(match_ssid, ssid->ssid, match_ssid_len) != 0)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID mismatch");
+		return false;
+	}
+
+	if (ssid->bssid_set &&
+	    os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID mismatch");
+		return false;
+	}
+
+	/* check blacklist */
+	if (ssid->num_bssid_blacklist &&
+	    addr_in_list(bss->bssid, ssid->bssid_blacklist,
+			 ssid->num_bssid_blacklist)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - BSSID blacklisted");
+		return false;
+	}
+
+	/* if there is a whitelist, only accept those APs */
+	if (ssid->num_bssid_whitelist &&
+	    !addr_in_list(bss->bssid, ssid->bssid_whitelist,
+			  ssid->num_bssid_whitelist)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - BSSID not in whitelist");
+		return false;
+	}
+
+	if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss, debug_print))
+		return false;
+
+	if (!osen && !wpa &&
+	    !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+	    !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+	    !(ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
+	    !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - non-WPA network not allowed");
+		return false;
+	}
+
+#ifdef CONFIG_WEP
+	if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) && has_wep_key(ssid)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - ignore WPA/WPA2 AP for WEP network block");
+		return false;
+	}
+#endif /* CONFIG_WEP */
+
+	if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen && !rsn_osen) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - non-OSEN network not allowed");
+		return false;
+	}
+
+	if (!wpa_supplicant_match_privacy(bss, ssid)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - privacy mismatch");
+		return false;
+	}
+
+	if (ssid->mode != WPAS_MODE_MESH && !bss_is_ess(bss) &&
+	    !bss_is_pbss(bss)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - not ESS, PBSS, or MBSS");
+		return false;
+	}
+
+	if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - PBSS mismatch (ssid %d bss %d)",
+				ssid->pbss, bss_is_pbss(bss));
+		return false;
+	}
+
+	if (!freq_allowed(ssid->freq_list, bss->freq)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - frequency not allowed");
+		return false;
+	}
+
+#ifdef CONFIG_MESH
+	if (ssid->mode == WPAS_MODE_MESH && ssid->frequency > 0 &&
+	    ssid->frequency != bss->freq) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - frequency not allowed (mesh)");
+		return false;
+	}
+#endif /* CONFIG_MESH */
+
+	if (!rate_match(wpa_s, ssid, bss, debug_print)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - rate sets do not match");
+		return false;
+	}
+
+#ifdef CONFIG_SAE
+	if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
+	    wpa_s->conf->sae_pwe != 3 && wpa_key_mgmt_sae(ssid->key_mgmt) &&
+	    !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - SAE H2E required, but not supported by the AP");
+		return false;
+	}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_SAE_PK
+	if (ssid->sae_pk == SAE_PK_MODE_ONLY &&
+	    !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK))) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - SAE-PK required, but not supported by the AP");
+		return false;
+	}
+#endif /* CONFIG_SAE_PK */
+
+#ifndef CONFIG_IBSS_RSN
+	if (ssid->mode == WPAS_MODE_IBSS &&
+	    !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - IBSS RSN not supported in the build");
+		return false;
+	}
+#endif /* !CONFIG_IBSS_RSN */
+
+#ifdef CONFIG_P2P
+	if (ssid->p2p_group &&
+	    !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+	    !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no P2P IE seen");
+		return false;
+	}
+
+	if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) {
+		struct wpabuf *p2p_ie;
+		u8 dev_addr[ETH_ALEN];
+
+		ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+		if (!ie) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - no P2P element");
+			return false;
+		}
+		p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+		if (!p2p_ie) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - could not fetch P2P element");
+			return false;
+		}
+
+		if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0 ||
+		    os_memcmp(dev_addr, ssid->go_p2p_dev_addr, ETH_ALEN) != 0) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - no matching GO P2P Device Address in P2P element");
+			wpabuf_free(p2p_ie);
+			return false;
+		}
+		wpabuf_free(p2p_ie);
+	}
+
+	/*
+	 * TODO: skip the AP if its P2P IE has Group Formation bit set in the
+	 * P2P Group Capability Bitmap and we are not in Group Formation with
+	 * that device.
+	 */
+#endif /* CONFIG_P2P */
+
+	if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time)) {
+		struct os_reltime diff;
+
+		os_reltime_sub(&wpa_s->scan_min_time, &bss->last_update, &diff);
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - scan result not recent enough (%u.%06u seconds too old)",
+				(unsigned int) diff.sec,
+				(unsigned int) diff.usec);
+		return false;
+	}
+#ifdef CONFIG_MBO
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->ignore_assoc_disallow)
+		goto skip_assoc_disallow;
+#endif /* CONFIG_TESTING_OPTIONS */
+	assoc_disallow = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_ASSOC_DISALLOW);
+	if (assoc_disallow && assoc_disallow[1] >= 1) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - MBO association disallowed (reason %u)",
+				assoc_disallow[2]);
+		return false;
+	}
+
+	if (wpa_is_bss_tmp_disallowed(wpa_s, bss)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - AP temporarily disallowed");
+		return false;
+	}
+#ifdef CONFIG_TESTING_OPTIONS
+skip_assoc_disallow:
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_DPP
+	if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
+	    !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
+	    (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+	     !ssid->dpp_csign)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - no PMKSA entry for DPP");
+		return false;
+	}
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_SAE_PK
+	if (ssid->sae_pk == SAE_PK_MODE_AUTOMATIC &&
+	    wpa_key_mgmt_sae(ssid->key_mgmt) &&
+	    ((ssid->sae_password &&
+	      sae_pk_valid_password(ssid->sae_password)) ||
+	     (!ssid->sae_password && ssid->passphrase &&
+	      sae_pk_valid_password(ssid->passphrase))) &&
+	    !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
+	    sae_pk_acceptable_bss_with_pk(wpa_s, bss, ssid, match_ssid,
+					  match_ssid_len)) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - another acceptable BSS with SAE-PK in the same ESS");
+		return false;
+	}
+#endif /* CONFIG_SAE_PK */
+
+	if (bss->ssid_len == 0) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - no SSID known for the BSS");
+		return false;
+	}
+
+	/* Matching configuration found */
+	return true;
+}
+
+
 struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 				     int i, struct wpa_bss *bss,
 				     struct wpa_ssid *group,
 				     int only_first_ssid, int debug_print)
 {
 	u8 wpa_ie_len, rsn_ie_len;
-	int wpa;
-	struct wpa_blacklist *e;
 	const u8 *ie;
 	struct wpa_ssid *ssid;
-	int osen, rsn_osen = 0;
-#ifdef CONFIG_MBO
-	const u8 *assoc_disallow;
-#endif /* CONFIG_MBO */
+	int osen;
 	const u8 *match_ssid;
 	size_t match_ssid_len;
-	struct wpa_ie_data data;
+	int blacklist_count;
 
 	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	wpa_ie_len = ie ? ie[1] : 0;
 
 	ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
 	rsn_ie_len = ie ? ie[1] : 0;
-	if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 &&
-	    (data.key_mgmt & WPA_KEY_MGMT_OSEN))
-		rsn_osen = 1;
 
 	ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
 	osen = ie != NULL;
@@ -1125,8 +1503,8 @@
 			osen ? " osen=1" : "");
 	}
 
-	e = wpa_blacklist_get(wpa_s, bss->bssid);
-	if (e) {
+	blacklist_count = wpa_blacklist_is_blacklisted(wpa_s, bss->bssid);
+	if (blacklist_count) {
 		int limit = 1;
 		if (wpa_supplicant_enabled_networks(wpa_s) == 1) {
 			/*
@@ -1139,11 +1517,11 @@
 			 */
 			limit = 0;
 		}
-		if (e->count > limit) {
+		if (blacklist_count > limit) {
 			if (debug_print) {
 				wpa_dbg(wpa_s, MSG_DEBUG,
 					"   skip - blacklisted (count=%d limit=%d)",
-					e->count, limit);
+					blacklist_count, limit);
 			}
 			return NULL;
 		}
@@ -1177,299 +1555,10 @@
 		return NULL;
 	}
 
-	wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
-
 	for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
-		int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
-		int res;
-
-		if (wpas_network_disabled(wpa_s, ssid)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled");
-			continue;
-		}
-
-		res = wpas_temp_disabled(wpa_s, ssid);
-		if (res > 0) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - disabled temporarily for %d second(s)",
-					res);
-			continue;
-		}
-
-#ifdef CONFIG_WPS
-		if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - blacklisted (WPS)");
-			continue;
-		}
-
-		if (wpa && ssid->ssid_len == 0 &&
-		    wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
-			check_ssid = 0;
-
-		if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
-			/* Only allow wildcard SSID match if an AP
-			 * advertises active WPS operation that matches
-			 * with our mode. */
-			check_ssid = 1;
-			if (ssid->ssid_len == 0 &&
-			    wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
-				check_ssid = 0;
-		}
-#endif /* CONFIG_WPS */
-
-		if (ssid->bssid_set && ssid->ssid_len == 0 &&
-		    os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
-			check_ssid = 0;
-
-		if (check_ssid &&
-		    (match_ssid_len != ssid->ssid_len ||
-		     os_memcmp(match_ssid, ssid->ssid, match_ssid_len) != 0)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - SSID mismatch");
-			continue;
-		}
-
-		if (ssid->bssid_set &&
-		    os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - BSSID mismatch");
-			continue;
-		}
-
-		/* check blacklist */
-		if (ssid->num_bssid_blacklist &&
-		    addr_in_list(bss->bssid, ssid->bssid_blacklist,
-				 ssid->num_bssid_blacklist)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - BSSID blacklisted");
-			continue;
-		}
-
-		/* if there is a whitelist, only accept those APs */
-		if (ssid->num_bssid_whitelist &&
-		    !addr_in_list(bss->bssid, ssid->bssid_whitelist,
-				  ssid->num_bssid_whitelist)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - BSSID not in whitelist");
-			continue;
-		}
-
-		if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss,
-						   debug_print))
-			continue;
-
-		if (!osen && !wpa &&
-		    !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
-		    !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
-		    !(ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
-		    !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - non-WPA network not allowed");
-			continue;
-		}
-
-#ifdef CONFIG_WEP
-		if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
-		    has_wep_key(ssid)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - ignore WPA/WPA2 AP for WEP network block");
-			continue;
-		}
-#endif /* CONFIG_WEP */
-
-		if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen &&
-		    !rsn_osen) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - non-OSEN network not allowed");
-			continue;
-		}
-
-		if (!wpa_supplicant_match_privacy(bss, ssid)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - privacy mismatch");
-			continue;
-		}
-
-		if (ssid->mode != WPAS_MODE_MESH && !bss_is_ess(bss) &&
-		    !bss_is_pbss(bss)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - not ESS, PBSS, or MBSS");
-			continue;
-		}
-
-		if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - PBSS mismatch (ssid %d bss %d)",
-					ssid->pbss, bss_is_pbss(bss));
-			continue;
-		}
-
-		if (!freq_allowed(ssid->freq_list, bss->freq)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - frequency not allowed");
-			continue;
-		}
-
-#ifdef CONFIG_MESH
-		if (ssid->mode == WPAS_MODE_MESH && ssid->frequency > 0 &&
-		    ssid->frequency != bss->freq) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - frequency not allowed (mesh)");
-			continue;
-		}
-#endif /* CONFIG_MESH */
-
-		if (!rate_match(wpa_s, ssid, bss, debug_print)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - rate sets do not match");
-			continue;
-		}
-
-#ifdef CONFIG_SAE
-		if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
-		    wpa_s->conf->sae_pwe != 3 &&
-		    wpa_key_mgmt_sae(ssid->key_mgmt) &&
-		    (!(ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX)) ||
-		     ie[1] < 1 ||
-		     !(ie[2] & BIT(WLAN_RSNX_CAPAB_SAE_H2E)))) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - SAE H2E required, but not supported by the AP");
-			continue;
-		}
-#endif /* CONFIG_SAE */
-
-#ifndef CONFIG_IBSS_RSN
-		if (ssid->mode == WPAS_MODE_IBSS &&
-		    !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE |
-					WPA_KEY_MGMT_WPA_NONE))) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - IBSS RSN not supported in the build");
-			continue;
-		}
-#endif /* !CONFIG_IBSS_RSN */
-
-#ifdef CONFIG_P2P
-		if (ssid->p2p_group &&
-		    !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
-		    !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - no P2P IE seen");
-			continue;
-		}
-
-		if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) {
-			struct wpabuf *p2p_ie;
-			u8 dev_addr[ETH_ALEN];
-
-			ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
-			if (ie == NULL) {
-				if (debug_print)
-					wpa_dbg(wpa_s, MSG_DEBUG,
-						"   skip - no P2P element");
-				continue;
-			}
-			p2p_ie = wpa_bss_get_vendor_ie_multi(
-				bss, P2P_IE_VENDOR_TYPE);
-			if (p2p_ie == NULL) {
-				if (debug_print)
-					wpa_dbg(wpa_s, MSG_DEBUG,
-						"   skip - could not fetch P2P element");
-				continue;
-			}
-
-			if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0
-			    || os_memcmp(dev_addr, ssid->go_p2p_dev_addr,
-					 ETH_ALEN) != 0) {
-				if (debug_print)
-					wpa_dbg(wpa_s, MSG_DEBUG,
-						"   skip - no matching GO P2P Device Address in P2P element");
-				wpabuf_free(p2p_ie);
-				continue;
-			}
-			wpabuf_free(p2p_ie);
-		}
-
-		/*
-		 * TODO: skip the AP if its P2P IE has Group Formation
-		 * bit set in the P2P Group Capability Bitmap and we
-		 * are not in Group Formation with that device.
-		 */
-#endif /* CONFIG_P2P */
-
-		if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time))
-		{
-			struct os_reltime diff;
-
-			os_reltime_sub(&wpa_s->scan_min_time,
-				       &bss->last_update, &diff);
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - scan result not recent enough (%u.%06u seconds too old)",
-				(unsigned int) diff.sec,
-				(unsigned int) diff.usec);
-			continue;
-		}
-#ifdef CONFIG_MBO
-#ifdef CONFIG_TESTING_OPTIONS
-		if (wpa_s->ignore_assoc_disallow)
-			goto skip_assoc_disallow;
-#endif /* CONFIG_TESTING_OPTIONS */
-		assoc_disallow = wpas_mbo_get_bss_attr(
-			bss, MBO_ATTR_ID_ASSOC_DISALLOW);
-		if (assoc_disallow && assoc_disallow[1] >= 1) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - MBO association disallowed (reason %u)",
-				assoc_disallow[2]);
-			continue;
-		}
-
-		if (wpa_is_bss_tmp_disallowed(wpa_s, bss)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - AP temporarily disallowed");
-			continue;
-		}
-#ifdef CONFIG_TESTING_OPTIONS
-	skip_assoc_disallow:
-#endif /* CONFIG_TESTING_OPTIONS */
-#endif /* CONFIG_MBO */
-
-#ifdef CONFIG_DPP
-		if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
-		    !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
-		    (!ssid->dpp_connector ||
-		     !ssid->dpp_netaccesskey ||
-		     !ssid->dpp_csign)) {
-			if (debug_print)
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   skip - no PMKSA entry for DPP");
-			continue;
-		}
-#endif /* CONFIG_DPP */
-
-		/* Matching configuration found */
-		return ssid;
+		if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
+				    bss, blacklist_count, debug_print))
+			return ssid;
 	}
 
 	/* No matching configuration found */
@@ -1769,52 +1858,19 @@
 	return wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr);
 }
 
-#endif /* CONFIG_NO_ROAMING */
 
-
-static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
-				       struct wpa_bss *selected,
-				       struct wpa_ssid *ssid)
+int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
+					   struct wpa_bss *current_bss,
+					   struct wpa_bss *selected)
 {
-	struct wpa_bss *current_bss = NULL;
-#ifndef CONFIG_NO_ROAMING
 	int min_diff, diff;
 	int to_5ghz;
 	int cur_level;
 	unsigned int cur_est, sel_est;
 	struct wpa_signal_info si;
 	int cur_snr = 0;
-#endif /* CONFIG_NO_ROAMING */
+	int ret = 0;
 
-	if (wpa_s->reassociate)
-		return 1; /* explicit request to reassociate */
-	if (wpa_s->wpa_state < WPA_ASSOCIATED)
-		return 1; /* we are not associated; continue */
-	if (wpa_s->current_ssid == NULL)
-		return 1; /* unknown current SSID */
-	if (wpa_s->current_ssid != ssid)
-		return 1; /* different network block */
-
-	if (wpas_driver_bss_selection(wpa_s))
-		return 0; /* Driver-based roaming */
-
-	if (wpa_s->current_ssid->ssid)
-		current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
-					  wpa_s->current_ssid->ssid,
-					  wpa_s->current_ssid->ssid_len);
-	if (!current_bss)
-		current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
-
-	if (!current_bss)
-		return 1; /* current BSS not seen in scan results */
-
-	if (current_bss == selected)
-		return 0;
-
-	if (selected->last_update_idx > current_bss->last_update_idx)
-		return 1; /* current BSS not seen in the last scan */
-
-#ifndef CONFIG_NO_ROAMING
 	wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
 	wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR
 		" freq=%d level=%d snr=%d est_throughput=%u",
@@ -1933,13 +1989,64 @@
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"Skip roam - too small difference in signal level (%d < %d)",
 			diff, min_diff);
-		return 0;
+		ret = 0;
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Allow reassociation due to difference in signal level (%d >= %d)",
+			diff, min_diff);
+		ret = 1;
 	}
+	wpa_msg_ctrl(wpa_s, MSG_INFO, "%scur_bssid=" MACSTR
+		     " cur_freq=%d cur_level=%d cur_est=%d sel_bssid=" MACSTR
+		     " sel_freq=%d sel_level=%d sel_est=%d",
+		     ret ? WPA_EVENT_DO_ROAM : WPA_EVENT_SKIP_ROAM,
+		     MAC2STR(current_bss->bssid),
+		     current_bss->freq, cur_level, cur_est,
+		     MAC2STR(selected->bssid),
+		     selected->freq, selected->level, sel_est);
+	return ret;
+}
 
-	wpa_dbg(wpa_s, MSG_DEBUG,
-		"Allow reassociation due to difference in signal level (%d >= %d)",
-		diff, min_diff);
-	return 1;
+#endif /* CONFIG_NO_ROAMING */
+
+
+static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
+				       struct wpa_bss *selected,
+				       struct wpa_ssid *ssid)
+{
+	struct wpa_bss *current_bss = NULL;
+
+	if (wpa_s->reassociate)
+		return 1; /* explicit request to reassociate */
+	if (wpa_s->wpa_state < WPA_ASSOCIATED)
+		return 1; /* we are not associated; continue */
+	if (wpa_s->current_ssid == NULL)
+		return 1; /* unknown current SSID */
+	if (wpa_s->current_ssid != ssid)
+		return 1; /* different network block */
+
+	if (wpas_driver_bss_selection(wpa_s))
+		return 0; /* Driver-based roaming */
+
+	if (wpa_s->current_ssid->ssid)
+		current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
+					  wpa_s->current_ssid->ssid,
+					  wpa_s->current_ssid->ssid_len);
+	if (!current_bss)
+		current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
+
+	if (!current_bss)
+		return 1; /* current BSS not seen in scan results */
+
+	if (current_bss == selected)
+		return 0;
+
+	if (selected->last_update_idx > current_bss->last_update_idx)
+		return 1; /* current BSS not seen in the last scan */
+
+#ifndef CONFIG_NO_ROAMING
+	return wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss,
+						      selected);
 #else /* CONFIG_NO_ROAMING */
 	return 0;
 #endif /* CONFIG_NO_ROAMING */
@@ -2571,11 +2678,11 @@
 {
 	int l, len, found = 0, found_x = 0, wpa_found, rsn_found;
 	const u8 *p;
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_OWE)
 	u8 bssid[ETH_ALEN];
-#endif /* CONFIG_IEEE80211R || CONFIG_OWE */
+	bool bssid_known;
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
+	bssid_known = wpa_drv_get_bssid(wpa_s, bssid) == 0;
 	if (data->assoc_info.req_ies)
 		wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
 			    data->assoc_info.req_ies_len);
@@ -2718,7 +2825,7 @@
 
 #ifdef CONFIG_OWE
 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
-	    (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+	    (!bssid_known ||
 	     owe_process_assoc_resp(wpa_s->wpa, bssid,
 				    data->assoc_info.resp_ies,
 				    data->assoc_info.resp_ies_len) < 0)) {
@@ -2753,7 +2860,7 @@
 #ifdef CONFIG_IEEE80211R
 #ifdef CONFIG_SME
 	if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
-		if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+		if (!bssid_known ||
 		    wpa_ft_validate_reassoc_resp(wpa_s->wpa,
 						 data->assoc_info.resp_ies,
 						 data->assoc_info.resp_ies_len,
@@ -2813,7 +2920,7 @@
 	/* Process FT when SME is in the driver */
 	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
 	    wpa_ft_is_completed(wpa_s->wpa)) {
-		if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+		if (!bssid_known ||
 		    wpa_ft_validate_reassoc_resp(wpa_s->wpa,
 						 data->assoc_info.resp_ies,
 						 data->assoc_info.resp_ies_len,
@@ -2831,6 +2938,11 @@
 			     data->assoc_info.resp_ies_len);
 #endif /* CONFIG_IEEE80211R */
 
+	if (bssid_known)
+		wpas_handle_assoc_resp_mscs(wpa_s, bssid,
+					    data->assoc_info.resp_ies,
+					    data->assoc_info.resp_ies_len);
+
 	/* WPA/RSN IE from Beacon/ProbeResp */
 	p = data->assoc_info.beacon_ies;
 	l = data->assoc_info.beacon_ies_len;
@@ -4164,6 +4276,13 @@
 	}
 #endif /* CONFIG_DPP */
 
+	if (category == WLAN_ACTION_ROBUST_AV_STREAMING &&
+	    payload[0] == ROBUST_AV_MSCS_RESP) {
+		wpas_handle_robust_av_recv_action(wpa_s, mgmt->sa,
+						  payload + 1, plen - 1);
+		return;
+	}
+
 	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
 			   category, payload, plen, freq);
 	if (wpa_s->ifmsh)
@@ -4402,7 +4521,9 @@
 	 * WLAN_STATUS_AKMP_NOT_VALID is addressed in the same manner as an
 	 * interoperability workaround with older hostapd implementation. */
 	if (DPP_VERSION > 1 && wpa_s->current_ssid &&
-	    wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP &&
+	    (wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP ||
+	     ((wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
+	      wpa_s->key_mgmt == WPA_KEY_MGMT_DPP)) &&
 	    wpa_s->current_ssid->dpp_pfs == 0 &&
 	    (data->assoc_reject.status_code ==
 	     WLAN_STATUS_ASSOC_DENIED_UNSPEC ||
@@ -4865,7 +4986,9 @@
 		}
 #endif /* CONFIG_AP */
 
-		sme_event_ch_switch(wpa_s);
+		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+			sme_event_ch_switch(wpa_s);
+
 		wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS);
 		wnm_clear_coloc_intf_reporting(wpa_s);
 		break;
@@ -5376,8 +5499,6 @@
 			return;
 		wpa_s = wpa_supplicant_add_iface(ctx, wpa_i, NULL);
 		os_free(wpa_i);
-		if (wpa_s)
-			wpa_s->matched = 1;
 	}
 #endif /* CONFIG_MATCH_IFACE */
 
diff --git a/wpa_supplicant/examples/dpp-nfc.py b/wpa_supplicant/examples/dpp-nfc.py
index 1883545..8e865f3 100755
--- a/wpa_supplicant/examples/dpp-nfc.py
+++ b/wpa_supplicant/examples/dpp-nfc.py
@@ -7,6 +7,8 @@
 # This software may be distributed under the terms of the BSD license.
 # See README for more details.
 
+import binascii
+import errno
 import os
 import struct
 import sys
@@ -29,20 +31,28 @@
 in_raw_mode = False
 prev_tcgetattr = 0
 no_input = False
-srv = None
 continue_loop = True
 terminate_now = False
 summary_file = None
 success_file = None
-my_crn_ready = False
-my_crn = None
-peer_crn = None
-hs_sent = False
+netrole = None
+operation_success = False
 mutex = threading.Lock()
 
-def summary(txt):
+C_NORMAL = '\033[0m'
+C_RED = '\033[91m'
+C_GREEN = '\033[92m'
+C_YELLOW = '\033[93m'
+C_BLUE = '\033[94m'
+C_MAGENTA = '\033[95m'
+C_CYAN = '\033[96m'
+
+def summary(txt, color=None):
     with mutex:
-        print(txt)
+        if color:
+            print(color + txt + C_NORMAL)
+        else:
+            print(txt)
         if summary_file:
             with open(summary_file, 'a') as f:
                 f.write(txt + "\n")
@@ -67,15 +77,18 @@
         return None
 
     for ctrl in ifaces:
-        if ifname:
-            if ifname not in ctrl:
-                continue
+        if ifname and ifname not in ctrl:
+            continue
+        if os.path.basename(ctrl).startswith("p2p-dev-"):
+            # skip P2P management interface
+            continue
         try:
             summary("Trying to use control interface " + ctrl)
             wpas = wpaspy.Ctrl(ctrl)
             return wpas
         except Exception as e:
             pass
+    summary("Could not connect to wpa_supplicant")
     return None
 
 def dpp_nfc_uri_process(uri):
@@ -84,7 +97,7 @@
         return False
     peer_id = wpas.request("DPP_NFC_URI " + uri)
     if "FAIL" in peer_id:
-        summary("Could not parse DPP URI from NFC URI record")
+        summary("Could not parse DPP URI from NFC URI record", color=C_RED)
         return False
     peer_id = int(peer_id)
     summary("peer_id=%d for URI from NFC Tag: %s" % (peer_id, uri))
@@ -99,7 +112,7 @@
     summary("Initiate DPP authentication: " + cmd)
     res = wpas.request(cmd)
     if "OK" not in res:
-        summary("Failed to initiate DPP Authentication")
+        summary("Failed to initiate DPP Authentication", color=C_RED)
         return False
     summary("DPP Authentication initiated")
     return True
@@ -110,20 +123,20 @@
         return False
     summary(record)
     if len(record.data) < 5:
-        summary("Too short DPP HS")
+        summary("Too short DPP HS", color=C_RED)
         return False
     if record.data[0] != 0:
-        summary("Unexpected URI Identifier Code")
+        summary("Unexpected URI Identifier Code", color=C_RED)
         return False
     uribuf = record.data[1:]
     try:
         uri = uribuf.decode()
     except:
-        summary("Invalid URI payload")
+        summary("Invalid URI payload", color=C_RED)
         return False
     summary("URI: " + uri)
     if not uri.startswith("DPP:"):
-        summary("Not a DPP URI")
+        summary("Not a DPP URI", color=C_RED)
         return False
     return dpp_nfc_uri_process(uri)
 
@@ -179,12 +192,43 @@
         raise Exception("Failed to generate bootstrapping info")
     return int(res)
 
-def wpas_get_nfc_uri(start_listen=True, pick_channel=False):
+def dpp_start_listen(wpas, freq):
+    if get_status_field(wpas, "bssid[0]"):
+        summary("Own AP freq: %s MHz" % str(get_status_field(wpas, "freq")))
+        if get_status_field(wpas, "beacon_set", extra="DRIVER") is None:
+            summary("Enable beaconing to have radio ready for RX")
+            wpas.request("DISABLE")
+            wpas.request("SET start_disabled 0")
+            wpas.request("ENABLE")
+    cmd = "DPP_LISTEN %d" % freq
+    global enrollee_only
+    global configurator_only
+    if enrollee_only:
+        cmd += " role=enrollee"
+    elif configurator_only:
+        cmd += " role=configurator"
+    global netrole
+    if netrole:
+        cmd += " netrole=" + netrole
+    summary(cmd)
+    res = wpas.request(cmd)
+    if "OK" not in res:
+        summary("Failed to start DPP listen", color=C_RED)
+        return False
+    return True
+
+def wpas_get_nfc_uri(start_listen=True, pick_channel=False, chan_override=None):
+    listen_freq = 2412
     wpas = wpas_connect()
     if wpas is None:
         return None
     global own_id, chanlist
-    chan = chanlist
+    if chan_override:
+        chan = chan_override
+    else:
+        chan = chanlist
+    if chan and chan.startswith("81/"):
+        listen_freq = int(chan[3:].split(',')[0]) * 5 + 2407
     if chan is None and get_status_field(wpas, "bssid[0]"):
         freq = get_status_field(wpas, "freq")
         if freq:
@@ -192,15 +236,18 @@
             if freq >= 2412 and freq <= 2462:
                 chan = "81/%d" % ((freq - 2407) / 5)
                 summary("Use current AP operating channel (%d MHz) as the URI channel list (%s)" % (freq, chan))
+                listen_freq = freq
     if chan is None and pick_channel:
         chan = "81/6"
         summary("Use channel 2437 MHz since no other preference provided")
+        listen_freq = 2437
     own_id = dpp_bootstrap_gen(wpas, type="nfc-uri", chan=chan, mac=True)
     res = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
     if "FAIL" in res:
         return None
     if start_listen:
-        wpas.request("DPP_LISTEN 2412 netrole=configurator")
+        if not dpp_start_listen(wpas, listen_freq):
+            raise Exception("Failed to start listen operation on %d MHz" % listen_freq)
     return res
 
 def wpas_report_handover_req(uri):
@@ -219,82 +266,180 @@
     cmd = "DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (own_id, uri)
     return wpas.request(cmd)
 
-def dpp_handover_client(llc):
-    uri = wpas_get_nfc_uri(start_listen=False)
+def dpp_handover_client(handover, alt=False):
+    summary("About to start run_dpp_handover_client (alt=%s)" % str(alt))
+    if alt:
+        handover.i_m_selector = False
+    run_dpp_handover_client(handover, alt)
+    summary("Done run_dpp_handover_client (alt=%s)" % str(alt))
+
+def run_client_alt(handover, alt):
+    if handover.start_client_alt and not alt:
+        handover.start_client_alt = False
+        summary("Try to send alternative handover request")
+        dpp_handover_client(handover, alt=True)
+
+class HandoverClient(nfc.handover.HandoverClient):
+    def __init__(self, handover, llc):
+        super(HandoverClient, self).__init__(llc)
+        self.handover = handover
+
+    def recv_records(self, timeout=None):
+        msg = self.recv_octets(timeout)
+        if msg is None:
+            return None
+        records = list(ndef.message_decoder(msg, 'relax'))
+        if records and records[0].type == 'urn:nfc:wkt:Hs':
+            summary("Handover client received message '{0}'".format(records[0].type))
+            return list(ndef.message_decoder(msg, 'relax'))
+        summary("Handover client received invalid message: %s" + binascii.hexlify(msg))
+        return None
+
+    def recv_octets(self, timeout=None):
+        start = time.time()
+        msg = bytearray()
+        while True:
+            poll_timeout = 0.1 if timeout is None or timeout > 0.1 else timeout
+            if not self.socket.poll('recv', poll_timeout):
+                if timeout:
+                    timeout -= time.time() - start
+                    if timeout <= 0:
+                        return None
+                    start = time.time()
+                continue
+            try:
+                r = self.socket.recv()
+                if r is None:
+                    return None
+                msg += r
+            except TypeError:
+                return b''
+            try:
+                list(ndef.message_decoder(msg, 'strict', {}))
+                return bytes(msg)
+            except ndef.DecodeError:
+                if timeout:
+                    timeout -= time.time() - start
+                    if timeout <= 0:
+                        return None
+                    start = time.time()
+                continue
+        return None
+
+def run_dpp_handover_client(handover, alt=False):
+    chan_override = None
+    if alt:
+        chan_override = handover.altchanlist
+        handover.alt_proposal_used = True
+    global test_uri, test_alt_uri
+    if test_uri:
+        summary("TEST MODE: Using specified URI (alt=%s)" % str(alt))
+        uri = test_alt_uri if alt else test_uri
+    else:
+        uri = wpas_get_nfc_uri(start_listen=False, chan_override=chan_override)
     if uri is None:
-        summary("Cannot start handover client - no bootstrap URI available")
+        summary("Cannot start handover client - no bootstrap URI available",
+                color=C_RED)
         return
+    handover.my_uri = uri
     uri = ndef.UriRecord(uri)
     summary("NFC URI record for DPP: " + str(uri))
     carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
-    crn = os.urandom(2)
+    global test_crn
+    if test_crn:
+        prev, = struct.unpack('>H', test_crn)
+        summary("TEST MODE: Use specified crn %d" % prev)
+        crn = test_crn
+        test_crn = struct.pack('>H', prev + 0x10)
+    else:
+        crn = os.urandom(2)
     hr = ndef.HandoverRequestRecord(version="1.4", crn=crn)
     hr.add_alternative_carrier('active', carrier.name)
     message = [hr, carrier]
     summary("NFC Handover Request message for DPP: " + str(message))
 
-    global peer_crn
-    if peer_crn is not None:
+    if handover.peer_crn is not None and not alt:
         summary("NFC handover request from peer was already received - do not send own")
         return
-    client = nfc.handover.HandoverClient(llc)
-    try:
-        summary("Trying to initiate NFC connection handover")
-        client.connect()
-        summary("Connected for handover")
-    except nfc.llcp.ConnectRefused:
-        summary("Handover connection refused")
-        client.close()
-        return
-    except Exception as e:
-        summary("Other exception: " + str(e))
-        client.close()
-        return
+    if handover.client:
+        summary("Use already started handover client")
+        client = handover.client
+    else:
+        summary("Start handover client")
+        client = HandoverClient(handover, handover.llc)
+        try:
+            summary("Trying to initiate NFC connection handover")
+            client.connect()
+            summary("Connected for handover")
+        except nfc.llcp.ConnectRefused:
+            summary("Handover connection refused")
+            client.close()
+            return
+        except Exception as e:
+            summary("Other exception: " + str(e))
+            client.close()
+            return
+        handover.client = client
 
-    if peer_crn is not None:
+    if handover.peer_crn is not None and not alt:
         summary("NFC handover request from peer was already received - do not send own")
-        client.close()
         return
 
     summary("Sending handover request")
 
-    global my_crn, my_crn_ready, hs_sent
-    my_crn_ready = True
+    handover.my_crn_ready = True
 
     if not client.send_records(message):
-        my_crn_ready = False
-        summary("Failed to send handover request")
-        client.close()
+        handover.my_crn_ready = False
+        summary("Failed to send handover request", color=C_RED)
+        run_client_alt(handover, alt)
         return
 
-    my_crn, = struct.unpack('>H', crn)
+    handover.my_crn, = struct.unpack('>H', crn)
 
     summary("Receiving handover response")
     try:
+        start = time.time()
         message = client.recv_records(timeout=3.0)
+        end = time.time()
+        summary("Received {} record(s) in {} seconds".format(len(message) if message is not None else -1, end - start))
     except Exception as e:
         # This is fine if we are the handover selector
-        if hs_sent:
+        if handover.hs_sent:
             summary("Client receive failed as expected since I'm the handover server: %s" % str(e))
+        elif handover.alt_proposal_used and not alt:
+            summary("Client received failed for initial proposal as expected since alternative proposal was also used: %s" % str(e))
         else:
-            summary("Client receive failed: %s" % str(e))
+            summary("Client receive failed: %s" % str(e), color=C_RED)
         message = None
     if message is None:
-        if hs_sent:
+        if handover.hs_sent:
             summary("No response received as expected since I'm the handover server")
+        elif handover.alt_proposal_used and not alt:
+            summary("No response received for initial proposal as expected since alternative proposal was also used")
+        elif handover.try_own and not alt:
+            summary("No response received for initial proposal as expected since alternative proposal will also be sent")
         else:
-            summary("No response received")
-        client.close()
+            summary("No response received", color=C_RED)
+        run_client_alt(handover, alt)
         return
     summary("Received message: " + str(message))
     if len(message) < 1 or \
        not isinstance(message[0], ndef.HandoverSelectRecord):
         summary("Response was not Hs - received: " + message.type)
-        client.close()
         return
 
     summary("Received handover select message")
     summary("alternative carriers: " + str(message[0].alternative_carriers))
+    if handover.i_m_selector:
+        summary("Ignore the received select since I'm the handover selector")
+        run_client_alt(handover, alt)
+        return
+
+    if handover.alt_proposal_used and not alt:
+        summary("Ignore received handover select for the initial proposal since alternative proposal was sent")
+        client.close()
+        return
 
     dpp_found = False
     for carrier in message:
@@ -303,15 +448,19 @@
         summary("Remote carrier type: " + carrier.type)
         if carrier.type == "application/vnd.wfa.dpp":
             if len(carrier.data) == 0 or carrier.data[0] != 0:
-                summary("URI Identifier Code 'None' not seen")
+                summary("URI Identifier Code 'None' not seen", color=C_RED)
                 continue
             summary("DPP carrier type match - send to wpa_supplicant")
             dpp_found = True
             uri = carrier.data[1:].decode("utf-8")
             summary("DPP URI: " + uri)
+            handover.peer_uri = uri
+            if test_uri:
+                summary("TEST MODE: Fake processing")
+                break
             res = wpas_report_handover_sel(uri)
             if res is None or "FAIL" in res:
-                summary("DPP handover report rejected")
+                summary("DPP handover report rejected", color=C_RED)
                 break
 
             success_report("DPP handover reported successfully (initiator)")
@@ -331,7 +480,7 @@
                 # TODO: Single Configurator instance
                 res = wpas.request("DPP_CONFIGURATOR_ADD")
                 if "FAIL" in res:
-                    summary("Failed to initiate Configurator")
+                    summary("Failed to initiate Configurator", color=C_RED)
                     break
                 conf_id = int(res)
                 extra = " conf=sta-dpp configurator=%d" % conf_id
@@ -341,17 +490,23 @@
             cmd += extra
             res = wpas.request(cmd)
             if "FAIL" in res:
-                summary("Failed to initiate DPP authentication")
+                summary("Failed to initiate DPP authentication", color=C_RED)
             break
 
-    if not dpp_found:
+    if not dpp_found and handover.no_alt_proposal:
+        summary("DPP carrier not seen in response - do not allow alternative proposal anymore")
+    elif not dpp_found:
         summary("DPP carrier not seen in response - allow peer to initiate a new handover with different parameters")
-        client.close()
+        handover.alt_proposal = True
+        handover.my_crn_ready = False
+        handover.my_crn = None
+        handover.peer_crn = None
+        handover.hs_sent = False
         summary("Returning from dpp_handover_client")
         return
 
     summary("Remove peer")
-    client.close()
+    handover.close()
     summary("Done with handover")
     global only_one
     if only_one:
@@ -360,22 +515,67 @@
         continue_loop = False
 
     global no_wait
-    if no_wait:
-        print("Trying to exit..")
+    if no_wait or only_one:
+        summary("Trying to exit..")
         global terminate_now
         terminate_now = True
 
     summary("Returning from dpp_handover_client")
 
 class HandoverServer(nfc.handover.HandoverServer):
-    def __init__(self, llc):
+    def __init__(self, handover, llc):
         super(HandoverServer, self).__init__(llc)
         self.sent_carrier = None
         self.ho_server_processing = False
         self.success = False
-        self.try_own = False
+        self.llc = llc
+        self.handover = handover
+
+    def serve(self, socket):
+        peer_sap = socket.getpeername()
+        summary("Serving handover client on remote sap {0}".format(peer_sap))
+        send_miu = socket.getsockopt(nfc.llcp.SO_SNDMIU)
+        try:
+            while socket.poll("recv"):
+                req = bytearray()
+                while socket.poll("recv"):
+                    r = socket.recv()
+                    if r is None:
+                        return None
+                    summary("Received %d octets" % len(r))
+                    req += r
+                    if len(req) == 0:
+                        continue
+                    try:
+                        list(ndef.message_decoder(req, 'strict', {}))
+                    except ndef.DecodeError:
+                        continue
+                    summary("Full message received")
+                    resp = self._process_request_data(req)
+                    if resp is None or len(resp) == 0:
+                        summary("No handover select to send out - wait for a possible alternative handover request")
+                        handover.alt_proposal = True
+                        req = bytearray()
+                        continue
+
+                    for offset in range(0, len(resp), send_miu):
+                        if not socket.send(resp[offset:offset + send_miu]):
+                            summary("Failed to send handover select - connection closed")
+                            return
+                    summary("Sent out full handover select")
+                    if handover.terminate_on_hs_send_completion:
+                        handover.delayed_exit()
+
+        except nfc.llcp.Error as e:
+            global terminate_now
+            summary("HandoverServer exception: %s" % e,
+                    color=None if e.errno == errno.EPIPE or terminate_now else C_RED)
+        finally:
+            socket.close()
+            summary("Handover serve thread exiting")
 
     def process_handover_request_message(self, records):
+        handover = self.handover
         self.ho_server_processing = True
         global in_raw_mode
         was_in_raw_mode = in_raw_mode
@@ -384,33 +584,33 @@
             print("\n")
         summary("HandoverServer - request received: " + str(records))
 
-        global my_crn, peer_crn, my_crn_ready
-
         for carrier in records:
             if not isinstance(carrier, ndef.HandoverRequestRecord):
                 continue
             if carrier.collision_resolution_number:
-                peer_crn = carrier.collision_resolution_number
-                summary("peer_crn: %d" % peer_crn)
+                handover.peer_crn = carrier.collision_resolution_number
+                summary("peer_crn: %d" % handover.peer_crn)
 
-        if my_crn is None and my_crn_ready:
+        if handover.my_crn is None and handover.my_crn_ready:
             summary("Still trying to send own handover request - wait a moment to see if that succeeds before checking crn values")
             for i in range(10):
-                if my_crn is not None:
+                if handover.my_crn is not None:
                     break
                 time.sleep(0.01)
-        if my_crn is not None:
-            summary("my_crn: %d" % my_crn)
+        if handover.my_crn is not None:
+            summary("my_crn: %d" % handover.my_crn)
 
-        if my_crn is not None and peer_crn is not None:
-            if my_crn == peer_crn:
+        if handover.my_crn is not None and handover.peer_crn is not None:
+            if handover.my_crn == handover.peer_crn:
                 summary("Same crn used - automatic collision resolution failed")
                 # TODO: Should generate a new Handover Request message
                 return ''
-            if ((my_crn & 1) == (peer_crn & 1) and my_crn > peer_crn) or \
-               ((my_crn & 1) != (peer_crn & 1) and my_crn < peer_crn):
+            if ((handover.my_crn & 1) == (handover.peer_crn & 1) and \
+                handover.my_crn > handover.peer_crn) or \
+               ((handover.my_crn & 1) != (handover.peer_crn & 1) and \
+                handover.my_crn < handover.peer_crn):
                 summary("I'm the Handover Selector Device")
-                pass
+                handover.i_m_selector = True
             else:
                 summary("Peer is the Handover Selector device")
                 summary("Ignore the received request.")
@@ -428,61 +628,73 @@
             if carrier.type == "application/vnd.wfa.dpp":
                 summary("DPP carrier type match - add DPP carrier record")
                 if len(carrier.data) == 0 or carrier.data[0] != 0:
-                    summary("URI Identifier Code 'None' not seen")
+                    summary("URI Identifier Code 'None' not seen", color=C_RED)
                     continue
                 uri = carrier.data[1:].decode("utf-8")
                 summary("Received DPP URI: " + uri)
 
-                data = wpas_get_nfc_uri(start_listen=False, pick_channel=True)
+                global test_uri, test_alt_uri
+                if test_uri:
+                    summary("TEST MODE: Using specified URI")
+                    data = test_sel_uri if test_sel_uri else test_uri
+                elif handover.alt_proposal and handover.altchanlist:
+                    summary("Use alternative channel list while processing alternative proposal from peer")
+                    data = wpas_get_nfc_uri(start_listen=False,
+                                            chan_override=handover.altchanlist,
+                                            pick_channel=True)
+                else:
+                    data = wpas_get_nfc_uri(start_listen=False,
+                                            pick_channel=True)
                 summary("Own URI (pre-processing): %s" % data)
 
-                res = wpas_report_handover_req(uri)
+                if test_uri:
+                    summary("TEST MODE: Fake processing")
+                    res = "OK"
+                    data += " [%s]" % uri
+                else:
+                    res = wpas_report_handover_req(uri)
                 if res is None or "FAIL" in res:
-                    summary("DPP handover request processing failed")
+                    summary("DPP handover request processing failed",
+                            color=C_RED)
+                    if handover.altchanlist:
+                        data = wpas_get_nfc_uri(start_listen=False,
+                                                chan_override=handover.altchanlist)
+                        summary("Own URI (try another channel list): %s" % data)
+                    continue
+
+                if test_alt_uri:
+                    summary("TEST MODE: Reject initial proposal")
                     continue
 
                 found = True
 
-                wpas = wpas_connect()
-                if wpas is None:
-                    continue
-                global own_id
-                data = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
-                if "FAIL" in data:
-                    continue
+                if not test_uri:
+                    wpas = wpas_connect()
+                    if wpas is None:
+                        continue
+                    global own_id
+                    data = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
+                    if "FAIL" in data:
+                        continue
                 summary("Own URI (post-processing): %s" % data)
+                handover.my_uri = data
+                handover.peer_uri = uri
                 uri = ndef.UriRecord(data)
                 summary("Own bootstrapping NFC URI record: " + str(uri))
 
-                info = wpas.request("DPP_BOOTSTRAP_INFO %d" % own_id)
-                freq = None
-                for line in info.splitlines():
-                    if line.startswith("use_freq="):
-                        freq = int(line.split('=')[1])
-                if freq is None or freq == 0:
-                    summary("No channel negotiated over NFC - use channel 6")
-                    freq = 2437
-                else:
-                    summary("Negotiated channel: %d MHz" % freq)
-                if get_status_field(wpas, "bssid[0]"):
-                    summary("Own AP freq: %s MHz" % str(get_status_field(wpas, "freq")))
-                    if get_status_field(wpas, "beacon_set", extra="DRIVER") is None:
-                        summary("Enable beaconing to have radio ready for RX")
-                        wpas.request("DISABLE")
-                        wpas.request("SET start_disabled 0")
-                        wpas.request("ENABLE")
-                cmd = "DPP_LISTEN %d" % freq
-                global enrollee_only
-                global configurator_only
-                if enrollee_only:
-                    cmd += " role=enrollee"
-                elif configurator_only:
-                    cmd += " role=configurator"
-                summary(cmd)
-                res = wpas.request(cmd)
-                if "OK" not in res:
-                    summary("Failed to start DPP listen")
-                    break
+                if not test_uri:
+                    info = wpas.request("DPP_BOOTSTRAP_INFO %d" % own_id)
+                    freq = None
+                    for line in info.splitlines():
+                        if line.startswith("use_freq="):
+                            freq = int(line.split('=')[1])
+                    if freq is None or freq == 0:
+                        summary("No channel negotiated over NFC - use channel 6")
+                        freq = 2437
+                    else:
+                        summary("Negotiated channel: %d MHz" % freq)
+                    if not dpp_start_listen(wpas, freq):
+                        break
 
                 carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
                 summary("Own DPP carrier record: " + str(carrier))
@@ -492,11 +704,26 @@
 
         summary("Sending handover select: " + str(sel))
         if found:
+            summary("Handover completed successfully")
+            handover.terminate_on_hs_send_completion = True
             self.success = True
+            handover.hs_sent = True
+            handover.i_m_selector = True
+        elif handover.no_alt_proposal:
+            summary("Do not try alternative proposal anymore - handover failed",
+                    color=C_RED)
+            handover.hs_sent = True
         else:
-            self.try_own = True
-        global hs_sent
-        hs_sent = True
+            summary("Try to initiate with alternative parameters")
+            handover.try_own = True
+            handover.hs_sent = False
+            handover.no_alt_proposal = True
+            if handover.client_thread:
+                handover.start_client_alt = True
+            else:
+                handover.client_thread = threading.Thread(target=llcp_worker,
+                                                          args=(self.llc, True))
+                handover.client_thread.start()
         return sel
 
 def clear_raw_mode():
@@ -553,17 +780,25 @@
 
 def rdwr_connected_write_tag(tag):
     summary("Tag found - writing - " + str(tag))
+    if not tag.ndef:
+        summary("Not a formatted NDEF tag", color=C_RED)
+        return
     if not tag.ndef.is_writeable:
-        summary("Not a writable tag")
+        summary("Not a writable tag", color=C_RED)
         return
     global dpp_tag_data
     if tag.ndef.capacity < len(dpp_tag_data):
         summary("Not enough room for the message")
         return
-    tag.ndef.records = dpp_tag_data
+    try:
+        tag.ndef.records = dpp_tag_data
+    except ValueError as e:
+        summary("Writing the tag failed: %s" % str(e), color=C_RED)
+        return
     success_report("Tag write succeeded")
-    summary("Done - remove tag")
-    global only_one
+    summary("Tag writing completed - remove tag", color=C_GREEN)
+    global only_one, operation_success
+    operation_success = True
     if only_one:
         global continue_loop
         continue_loop = False
@@ -574,7 +809,7 @@
     summary("Write NFC URI record")
     data = wpas_get_nfc_uri()
     if data is None:
-        summary("Could not get NFC URI from wpa_supplicant")
+        summary("Could not get NFC URI from wpa_supplicant", color=C_RED)
         return
 
     global dpp_sel_wait_remove
@@ -583,7 +818,7 @@
     uri = ndef.UriRecord(data)
     summary(uri)
 
-    summary("Touch an NFC tag")
+    summary("Touch an NFC tag to write URI record", color=C_CYAN)
     global dpp_tag_data
     dpp_tag_data = [uri]
     clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
@@ -592,7 +827,7 @@
     summary("Write NFC Handover Select record on a tag")
     data = wpas_get_nfc_uri()
     if data is None:
-        summary("Could not get NFC URI from wpa_supplicant")
+        summary("Could not get NFC URI from wpa_supplicant", color=C_RED)
         return
 
     global dpp_sel_wait_remove
@@ -606,7 +841,7 @@
     summary(hs)
     summary(carrier)
 
-    summary("Touch an NFC tag")
+    summary("Touch an NFC tag to write HS record", color=C_CYAN)
     global dpp_tag_data
     dpp_tag_data = [hs, carrier]
     summary(dpp_tag_data)
@@ -624,17 +859,24 @@
             global continue_loop
             continue_loop = False
     else:
-        summary("Not an NDEF tag - remove tag")
+        summary("Not an NDEF tag - remove tag", color=C_RED)
         return True
 
     return not no_wait
 
-def llcp_worker(llc):
+def llcp_worker(llc, try_alt):
+    global handover
+    print("Start of llcp_worker()")
+    if try_alt:
+        summary("Starting handover client (try_alt)")
+        dpp_handover_client(handover, alt=True)
+        summary("Exiting llcp_worker thread (try_alt)")
+        return
     global init_on_touch
     if init_on_touch:
-        summary("Starting handover client")
-        dpp_handover_client(llc)
-        summary("Exiting llcp_worker thread (init_in_touch)")
+        summary("Starting handover client (init_on_touch)")
+        dpp_handover_client(handover)
+        summary("Exiting llcp_worker thread (init_on_touch)")
         return
 
     global no_input
@@ -642,16 +884,18 @@
         summary("Wait for handover to complete")
     else:
         print("Wait for handover to complete - press 'i' to initiate")
-    global srv
-    global wait_connection
-    while not wait_connection and srv.sent_carrier is None:
-        if srv.try_own:
-            srv.try_own = False
+    while not handover.wait_connection and handover.srv.sent_carrier is None:
+        if handover.try_own:
+            handover.try_own = False
             summary("Try to initiate another handover with own parameters")
-            dpp_handover_client(llc)
+            handover.my_crn_ready = False
+            handover.my_crn = None
+            handover.peer_crn = None
+            handover.hs_sent = False
+            dpp_handover_client(handover, alt=True)
             summary("Exiting llcp_worker thread (retry with own parameters)")
             return
-        if srv.ho_server_processing:
+        if handover.srv.ho_server_processing:
             time.sleep(0.025)
         elif no_input:
             time.sleep(0.5)
@@ -661,7 +905,7 @@
                 continue
             clear_raw_mode()
             summary("Starting handover client")
-            dpp_handover_client(llc)
+            dpp_handover_client(handover)
             summary("Exiting llcp_worker thread (manual init)")
             return
 
@@ -672,28 +916,73 @@
         print("\r")
     summary("Exiting llcp_worker thread")
 
+class ConnectionHandover():
+    def __init__(self):
+        self.client = None
+        self.client_thread = None
+        self.reset()
+        self.exit_thread = None
+
+    def reset(self):
+        self.wait_connection = False
+        self.my_crn_ready = False
+        self.my_crn = None
+        self.peer_crn = None
+        self.hs_sent = False
+        self.no_alt_proposal = False
+        self.alt_proposal_used = False
+        self.i_m_selector = False
+        self.start_client_alt = False
+        self.terminate_on_hs_send_completion = False
+        self.try_own = False
+        self.my_uri = None
+        self.peer_uri = None
+        self.connected = False
+        self.alt_proposal = False
+
+    def start_handover_server(self, llc):
+        summary("Start handover server")
+        self.llc = llc
+        self.srv = HandoverServer(self, llc)
+
+    def close(self):
+        if self.client:
+            self.client.close()
+            self.client = None
+
+    def run_delayed_exit(self):
+        summary("Trying to exit (delayed)..")
+        time.sleep(0.25)
+        summary("Trying to exit (after wait)..")
+        global terminate_now
+        terminate_now = True
+
+    def delayed_exit(self):
+        global only_one
+        if only_one:
+            self.exit_thread = threading.Thread(target=self.run_delayed_exit)
+            self.exit_thread.start()
+
 def llcp_startup(llc):
-    summary("Start LLCP server")
-    global srv
-    srv = HandoverServer(llc)
+    global handover
+    handover.start_handover_server(llc)
     return llc
 
 def llcp_connected(llc):
     summary("P2P LLCP connected")
-    global wait_connection, my_crn, peer_crn, my_crn_ready, hs_sent
-    wait_connection = False
-    my_crn_ready = False
-    my_crn = None
-    peer_crn = None
-    hs_sent = False
-    global srv
-    srv.start()
+    global handover
+    handover.connected = True
+    handover.srv.start()
     if init_on_touch or not no_input:
-        threading.Thread(target=llcp_worker, args=(llc,)).start()
+        handover.client_thread = threading.Thread(target=llcp_worker,
+                                                  args=(llc, False))
+        handover.client_thread.start()
     return True
 
 def llcp_release(llc):
     summary("LLCP release")
+    global handover
+    handover.close()
     return True
 
 def terminate_loop():
@@ -737,22 +1026,53 @@
                         help='success file for writing success update')
     parser.add_argument('--device', default='usb', help='NFC device to open')
     parser.add_argument('--chan', default=None, help='channel list')
+    parser.add_argument('--altchan', default=None, help='alternative channel list')
+    parser.add_argument('--netrole', default=None, help='netrole for Enrollee')
+    parser.add_argument('--test-uri', default=None,
+                        help='test mode: initial URI')
+    parser.add_argument('--test-alt-uri', default=None,
+                        help='test mode: alternative URI')
+    parser.add_argument('--test-sel-uri', default=None,
+                        help='test mode: handover select URI')
+    parser.add_argument('--test-crn', default=None,
+                        help='test mode: hardcoded crn')
     parser.add_argument('command', choices=['write-nfc-uri',
                                             'write-nfc-hs'],
                         nargs='?')
     args = parser.parse_args()
     summary(args)
 
+    global handover
+    handover = ConnectionHandover()
+
     global only_one
     only_one = args.only_one
 
     global no_wait
     no_wait = args.no_wait
 
-    global chanlist
+    global chanlist, netrole, test_uri, test_alt_uri, test_sel_uri
+    global test_crn
     chanlist = args.chan
+    handover.altchanlist = args.altchan
+    netrole = args.netrole
+    test_uri = args.test_uri
+    test_alt_uri = args.test_alt_uri
+    test_sel_uri = args.test_sel_uri
+    if args.test_crn:
+        test_crn = struct.pack('>H', int(args.test_crn))
+    else:
+        test_crn = None
 
     logging.basicConfig(level=args.loglevel)
+    for l in ['nfc.clf.rcs380',
+              'nfc.clf.transport',
+              'nfc.clf.device',
+              'nfc.clf.__init__',
+              'nfc.llcp',
+              'nfc.handover']:
+        log = logging.getLogger(l)
+        log.setLevel(args.loglevel)
 
     global init_on_touch
     init_on_touch = args.init_on_touch
@@ -788,19 +1108,22 @@
         no_input = True
 
     clf = nfc.ContactlessFrontend()
-    global wait_connection
 
     try:
         if not clf.open(args.device):
-            summary("Could not open connection with an NFC device")
-            raise SystemExit
+            summary("Could not open connection with an NFC device", color=C_RED)
+            raise SystemExit(1)
 
         if args.command == "write-nfc-uri":
             write_nfc_uri(clf, wait_remove=not args.no_wait)
+            if not operation_success:
+                raise SystemExit(1)
             raise SystemExit
 
         if args.command == "write-nfc-hs":
             write_nfc_hs(clf, wait_remove=not args.no_wait)
+            if not operation_success:
+                raise SystemExit(1)
             raise SystemExit
 
         global continue_loop
@@ -810,8 +1133,14 @@
             clear_raw_mode()
             if was_in_raw_mode:
                 print("\r")
-            summary("Waiting for a tag or peer to be touched")
-            wait_connection = True
+            if args.handover_only:
+                summary("Waiting a peer to be touched", color=C_MAGENTA)
+            elif args.tag_read_only:
+                summary("Waiting for a tag to be touched", color=C_BLUE)
+            else:
+                summary("Waiting for a tag or peer to be touched",
+                        color=C_GREEN)
+            handover.wait_connection = True
             try:
                 if args.tag_read_only:
                     if not clf.connect(rdwr={'on-connect': rdwr_connected}):
@@ -833,9 +1162,18 @@
                 summary("clf.connect failed: " + str(e))
                 break
 
-            global srv
-            if only_one and srv and srv.success:
-                raise SystemExit
+            if only_one and handover.connected:
+                role = "selector" if handover.i_m_selector else "requestor"
+                summary("Connection handover result: I'm the %s" % role,
+                        color=C_YELLOW)
+                if handover.peer_uri:
+                    summary("Peer URI: " + handover.peer_uri, color=C_YELLOW)
+                if handover.my_uri:
+                    summary("My URI: " + handover.my_uri, color=C_YELLOW)
+                if not (handover.peer_uri and handover.my_uri):
+                    summary("Negotiated connection handover failed",
+                            color=C_YELLOW)
+                break
 
     except KeyboardInterrupt:
         raise SystemExit
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 4b3fcfc..e60a8c1 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -694,13 +694,15 @@
 		return;
 	}
 
-	if (!query->maintain_addr &&
-	    wpas_update_random_addr_disassoc(wpa_s) < 0) {
-		wpa_msg(wpa_s, MSG_INFO,
-			"Failed to assign random MAC address for GAS");
-		gas_query_free(query, 1);
-		radio_work_done(work);
-		return;
+	if (!query->maintain_addr && !wpa_s->conf->gas_rand_mac_addr) {
+		if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"Failed to assign random MAC address for GAS");
+			gas_query_free(query, 1);
+			radio_work_done(work);
+			return;
+		}
+		os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN);
 	}
 
 	gas->work = work;
diff --git a/wpa_supplicant/hidl/1.4/hidl.cpp b/wpa_supplicant/hidl/1.4/hidl.cpp
index c664e15..0145576 100644
--- a/wpa_supplicant/hidl/1.4/hidl.cpp
+++ b/wpa_supplicant/hidl/1.4/hidl.cpp
@@ -250,13 +250,13 @@
 void wpas_hidl_notify_hs20_rx_deauth_imminent_notice(
     struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url)
 {
-	if (!wpa_s || !wpa_s->global->hidl || !url)
+	if (!wpa_s || !wpa_s->global->hidl)
 		return;
 
 	wpa_printf(
 	    MSG_DEBUG,
 	    "Notifying HS20 deauth imminent notice rx to hidl control: %s",
-	    url);
+	    url ? url : "<no URL>");
 
 	HidlManager *hidl_manager = HidlManager::getInstance();
 	if (!hidl_manager)
@@ -266,6 +266,23 @@
 	    wpa_s, code, reauth_delay, url);
 }
 
+void wpas_hidl_notify_hs20_rx_terms_and_conditions_acceptance(
+		struct wpa_supplicant *wpa_s, const char *url)
+{
+	if (!wpa_s || !wpa_s->global->hidl || !url)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+			"Notifying HS20 terms and conditions acceptance rx to hidl control: %s",
+			url);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyHs20RxTermsAndConditionsAcceptance(wpa_s, url);
+}
+
 void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s)
 {
 	if (!wpa_s)
@@ -876,3 +893,17 @@
 
 	hidl_manager->notifyBssTmStatus(wpa_s);
 }
+
+void wpas_hidl_notify_transition_disable(struct wpa_supplicant *wpa_s,
+					    struct wpa_ssid *ssid,
+					    u8 bitmap)
+{
+	if (!wpa_s || !ssid)
+		return;
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyTransitionDisable(wpa_s, ssid, bitmap);
+}
diff --git a/wpa_supplicant/hidl/1.4/hidl.h b/wpa_supplicant/hidl/1.4/hidl.h
index bf03bf9..671dd41 100644
--- a/wpa_supplicant/hidl/1.4/hidl.h
+++ b/wpa_supplicant/hidl/1.4/hidl.h
@@ -49,6 +49,8 @@
 	void wpas_hidl_notify_hs20_rx_deauth_imminent_notice(
 	    struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay,
 	    const char *url);
+	void wpas_hidl_notify_hs20_rx_terms_and_conditions_acceptance(
+			struct wpa_supplicant *wpa_s, const char *url);
 	void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
 	void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s,
 	    const u8 *bssid, u8 timed_out);
@@ -121,6 +123,8 @@
 	void wpas_hidl_notify_pmk_cache_added(
 	    struct wpa_supplicant *wpas, struct rsn_pmksa_cache_entry *pmksa_entry);
 	void wpas_hidl_notify_bss_tm_status(struct wpa_supplicant *wpa_s);
+	void wpas_hidl_notify_transition_disable(
+	    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, u8 bitmap);
 #else   // CONFIG_CTRL_IFACE_HIDL
 static inline int wpas_hidl_register_interface(struct wpa_supplicant *wpa_s)
 {
@@ -164,6 +168,9 @@
 static void wpas_hidl_notify_hs20_rx_deauth_imminent_notice(
     struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url)
 {}
+void wpas_hidl_notify_hs20_rx_terms_and_conditions_acceptance(
+		struct wpa_supplicant *wpa_s, const char *url)
+{}
 static void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s) {}
 static void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s,
     const u8 *bssid, u8 timed_out) {}
@@ -263,6 +270,10 @@
 {}
 void wpas_hidl_notify_bss_tm_status(struct wpa_supplicant *wpa_s)
 {}
+static void wpas_hidl_notify_transition_disable(struct wpa_supplicant *wpa_s,
+					    struct wpa_ssid *ssid,
+					    u8 bitmap)
+{}
 #endif  // CONFIG_CTRL_IFACE_HIDL
 
 #ifdef _cplusplus
diff --git a/wpa_supplicant/hidl/1.4/hidl_manager.cpp b/wpa_supplicant/hidl/1.4/hidl_manager.cpp
index b44f0fd..d7136a6 100644
--- a/wpa_supplicant/hidl/1.4/hidl_manager.cpp
+++ b/wpa_supplicant/hidl/1.4/hidl_manager.cpp
@@ -323,6 +323,38 @@
 	}
 }
 
+template <class CallbackTypeBase, class CallbackTypeDerived>
+void callWithEachNetworkCallbackDerived(
+    const std::string &ifname, int network_id,
+    const std::function<
+	android::hardware::Return<void>(android::sp<CallbackTypeDerived>)> &method,
+    const std::map<
+	const std::string, std::vector<android::sp<CallbackTypeBase>>>
+	&callbacks_map)
+{
+	if (ifname.empty())
+		return;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(ifname, network_id);
+	auto network_callback_map_iter = callbacks_map.find(network_key);
+	if (network_callback_map_iter == callbacks_map.end())
+		return;
+	const auto &network_callback_list = network_callback_map_iter->second;
+	for (const auto &callback : network_callback_list) {
+		android::sp<CallbackTypeDerived> callback_derived =
+		    CallbackTypeDerived::castFrom(callback);
+		if (callback_derived == nullptr)
+			continue;
+		if (!method(callback_derived).isOk()) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Failed to invoke HIDL network callback");
+		}
+	}
+}
+
 int parseGsmAuthNetworkRequest(
     const std::string &params_str,
     std::vector<hidl_array<uint8_t, kGsmRandLenBytes>> *out_rands)
@@ -888,18 +920,18 @@
 }
 
 /**
- * Notify all listeners about the reception of HS20 immient deauth
+ * Notify all listeners about the reception of HS20 imminent deauth
  * notification from the server.
  *
  * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
  * @param code Deauth reason code sent from server.
  * @param reauth_delay Reauthentication delay in seconds sent from server.
- * @param url URL of the server.
+ * @param url URL of the server containing the reason text.
  */
 void HidlManager::notifyHs20RxDeauthImminentNotice(
     struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url)
 {
-	if (!wpa_s || !url)
+	if (!wpa_s)
 		return;
 
 	if (sta_iface_object_map_.find(wpa_s->ifname) ==
@@ -914,6 +946,30 @@
 }
 
 /**
+ * Notify all listeners about the reception of HS20 terms and conditions
+ * acceptance notification from the server.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
+ * @param url URL of the T&C server.
+ */
+void HidlManager::notifyHs20RxTermsAndConditionsAcceptance(
+    struct wpa_supplicant *wpa_s, const char *url)
+{
+	if (!wpa_s || !url)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname)
+			== sta_iface_object_map_.end())
+		return;
+
+	callWithEachStaIfaceCallback_1_4(wpa_s->ifname,
+			std::bind(
+					&V1_4::ISupplicantStaIfaceCallback
+					::onHs20TermsAndConditionsAcceptanceRequestedNotification,
+					std::placeholders::_1, wpa_s->bssid, url));
+}
+
+/**
  * Notify all listeners about the reason code for disconnection from the
  * currently connected network.
  *
@@ -1737,6 +1793,56 @@
 #endif
 }
 
+uint32_t setTransitionDisableFlagsMask(u8 bitmap)
+{
+	uint32_t flags = 0;
+
+	if (bitmap & TRANSITION_DISABLE_WPA3_PERSONAL) {
+		flags |= V1_4::ISupplicantStaNetworkCallback::
+			TransitionDisableIndication::
+			USE_WPA3_PERSONAL;
+		bitmap &= ~TRANSITION_DISABLE_WPA3_PERSONAL;
+	}
+	if (bitmap & TRANSITION_DISABLE_SAE_PK) {
+		flags |= V1_4::ISupplicantStaNetworkCallback::
+			TransitionDisableIndication::
+			USE_SAE_PK;
+		bitmap &= ~TRANSITION_DISABLE_SAE_PK;
+	}
+	if (bitmap & TRANSITION_DISABLE_WPA3_ENTERPRISE) {
+		flags |= V1_4::ISupplicantStaNetworkCallback::
+			TransitionDisableIndication::
+			USE_WPA3_ENTERPRISE;
+		bitmap &= ~TRANSITION_DISABLE_WPA3_ENTERPRISE;
+	}
+	if (bitmap & TRANSITION_DISABLE_ENHANCED_OPEN) {
+		flags |= V1_4::ISupplicantStaNetworkCallback::
+			TransitionDisableIndication::
+			USE_ENHANCED_OPEN;
+		bitmap &= ~TRANSITION_DISABLE_ENHANCED_OPEN;
+	}
+
+	if (bitmap != 0) {
+		wpa_printf(MSG_WARNING, "Unhandled transition disable bit: 0x%x", bitmap);
+	}
+
+	return flags;
+}
+
+void HidlManager::notifyTransitionDisable(struct wpa_supplicant *wpa_s,
+    struct wpa_ssid *ssid, u8 bitmap)
+{
+	uint32_t flag = setTransitionDisableFlagsMask(bitmap);
+	const std::function<
+	    Return<void>(android::sp<V1_4::ISupplicantStaNetworkCallback>)>
+	    func = std::bind(
+		&V1_4::ISupplicantStaNetworkCallback::onTransitionDisable,
+		std::placeholders::_1, flag);
+
+	callWithEachStaNetworkCallbackDerived(wpa_s->ifname, ssid->id, func);
+}
+
+
 /**
  * Retrieve the |ISupplicantP2pIface| hidl object reference using the provided
  * ifname.
@@ -2123,6 +2229,23 @@
 
 /**
  * Helper function to invoke the provided callback method on all the
+ * registered V1.4 interface callback hidl objects for the specified
+ * |ifname|.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param method Pointer to the required hidl method from
+ * |V1_4::ISupplicantIfaceCallback|.
+ */
+void HidlManager::callWithEachStaIfaceCallback_1_4(
+    const std::string &ifname,
+    const std::function<
+	Return<void>(android::sp<V1_4::ISupplicantStaIfaceCallback>)> &method)
+{
+	callWithEachIfaceCallbackDerived(ifname, method, sta_iface_callbacks_map_);
+}
+
+/**
+ * Helper function to invoke the provided callback method on all the
  * registered derived interface callback hidl objects for the specified
  * |ifname|.
  *
@@ -2193,6 +2316,24 @@
 	callWithEachNetworkCallback(
 	    ifname, network_id, method, sta_network_callbacks_map_);
 }
+
+/**
+ * Helper function to invoke the provided callback method on all the
+ * registered derived interface callback hidl objects for the specified
+ * |ifname|.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param method Pointer to the required hidl method from
+ * derived |V1_x::ISupplicantIfaceCallback|.
+ */
+template <class CallbackTypeDerived>
+void HidlManager::callWithEachStaNetworkCallbackDerived(
+    const std::string &ifname, int network_id,
+    const std::function<
+	Return<void>(android::sp<CallbackTypeDerived>)> &method)
+{
+	callWithEachNetworkCallbackDerived(ifname, network_id, method, sta_network_callbacks_map_);
+}
 }  // namespace implementation
 }  // namespace V1_4
 }  // namespace supplicant
diff --git a/wpa_supplicant/hidl/1.4/hidl_manager.h b/wpa_supplicant/hidl/1.4/hidl_manager.h
index 496ceae..e3d6312 100644
--- a/wpa_supplicant/hidl/1.4/hidl_manager.h
+++ b/wpa_supplicant/hidl/1.4/hidl_manager.h
@@ -18,6 +18,7 @@
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pNetworkCallback.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantStaIfaceCallback.h>
 #include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetworkCallback.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantStaNetworkCallback.h>
 
 #include "p2p_iface.h"
 #include "p2p_network.h"
@@ -101,7 +102,9 @@
 	    struct wpa_supplicant *wpa_s, const char *url, u8 osu_method);
 	void notifyHs20RxDeauthImminentNotice(
 	    struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay,
-	    const char *url);
+		const char *url);
+	void notifyHs20RxTermsAndConditionsAcceptance(
+			struct wpa_supplicant *wpa_s, const char *url);
 	void notifyDisconnectReason(struct wpa_supplicant *wpa_s);
 	void notifyAssocReject(struct wpa_supplicant *wpa_s,
 	    const u8 *bssid, u8 timed_out);
@@ -166,6 +169,9 @@
 	void notifyPmkCacheAdded(struct wpa_supplicant *wpa_s,
 			struct rsn_pmksa_cache_entry *pmksa_entry);
 	void notifyBssTmStatus(struct wpa_supplicant *wpa_s);
+	void notifyTransitionDisable(struct wpa_supplicant *wpa_s,
+			struct wpa_ssid *ssid,
+			u8 bitmap);
 
 	// Methods called from hidl objects.
 	void notifyExtRadioWorkStart(struct wpa_supplicant *wpa_s, uint32_t id);
@@ -245,6 +251,10 @@
 	    const std::string &ifname,
 	    const std::function<android::hardware::Return<void>(
 	    android::sp<V1_3::ISupplicantStaIfaceCallback>)> &method);
+	void callWithEachStaIfaceCallback_1_4(
+	    const std::string &ifname,
+	    const std::function<android::hardware::Return<void>(
+	    android::sp<V1_4::ISupplicantStaIfaceCallback>)> &method);
 	template <class CallbackTypeDerived>
 	void callWithEachStaIfaceCallbackDerived(
 	    const std::string &ifname,
@@ -258,6 +268,11 @@
 	    const std::string &ifname, int network_id,
 	    const std::function<android::hardware::Return<void>(
 		android::sp<ISupplicantStaNetworkCallback>)> &method);
+	template <class CallbackTypeDerived>
+	void callWithEachStaNetworkCallbackDerived(
+	    const std::string &ifname, int network_id,
+	    const std::function<
+		Return<void>(android::sp<CallbackTypeDerived>)> &method);
 
 	// Singleton instance of this class.
 	static HidlManager *instance_;
diff --git a/wpa_supplicant/hidl/1.4/hidl_return_util.h b/wpa_supplicant/hidl/1.4/hidl_return_util.h
index d285164..217f215 100644
--- a/wpa_supplicant/hidl/1.4/hidl_return_util.h
+++ b/wpa_supplicant/hidl/1.4/hidl_return_util.h
@@ -17,7 +17,6 @@
 namespace V1_4 {
 namespace implementation {
 namespace hidl_return_util {
-using V1_0::SupplicantStatusCode;
 
 /**
  * These utility functions are used to invoke a method on the provided
@@ -30,10 +29,11 @@
  * provided error status and default values.
  */
 // Use for HIDL methods which return only an instance of SupplicantStatus.
-template <typename ObjT, typename WorkFuncT, typename... Args>
+template <typename ObjT, typename SupplicantStatusCodeT, typename WorkFuncT,
+    typename SupplicantStatusT, typename... Args>
 Return<void> validateAndCall(
-    ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
-    const std::function<void(const SupplicantStatus&)>& hidl_cb, Args&&... args)
+    ObjT* obj, SupplicantStatusCodeT status_code_if_invalid, WorkFuncT&& work,
+    const std::function<void(const SupplicantStatusT&)>& hidl_cb, Args&&... args)
 {
 	if (obj->isValid()) {
 		hidl_cb((obj->*work)(std::forward<Args>(args)...));
@@ -45,16 +45,17 @@
 
 // Use for HIDL methods which return instance of SupplicantStatus and a single
 // return value.
-template <typename ObjT, typename WorkFuncT, typename ReturnT, typename... Args>
+template <typename ObjT, typename SupplicantStatusCodeT, typename WorkFuncT,
+    typename SupplicantStatusT, typename ReturnT, typename... Args>
 Return<void> validateAndCall(
-    ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
-    const std::function<void(const SupplicantStatus&, ReturnT)>& hidl_cb,
+    ObjT* obj, SupplicantStatusCodeT status_code_if_invalid, WorkFuncT&& work,
+    const std::function<void(const SupplicantStatusT&, ReturnT)>& hidl_cb,
     Args&&... args)
 {
 	if (obj->isValid()) {
 		const auto& ret_pair =
 		    (obj->*work)(std::forward<Args>(args)...);
-		const SupplicantStatus& status = std::get<0>(ret_pair);
+		const SupplicantStatusT& status = std::get<0>(ret_pair);
 		const auto& ret_value = std::get<1>(ret_pair);
 		hidl_cb(status, ret_value);
 	} else {
@@ -68,18 +69,18 @@
 // Use for HIDL methods which return instance of SupplicantStatus and 2 return
 // values.
 template <
-    typename ObjT, typename WorkFuncT, typename ReturnT1, typename ReturnT2,
-    typename... Args>
+    typename ObjT, typename SupplicantStatusCodeT, typename WorkFuncT,
+    typename SupplicantStatusT, typename ReturnT1, typename ReturnT2, typename... Args>
 Return<void> validateAndCall(
-    ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
-    const std::function<void(const SupplicantStatus&, ReturnT1, ReturnT2)>&
+    ObjT* obj, SupplicantStatusCodeT status_code_if_invalid, WorkFuncT&& work,
+    const std::function<void(const SupplicantStatusT&, ReturnT1, ReturnT2)>&
 	hidl_cb,
     Args&&... args)
 {
 	if (obj->isValid()) {
 		const auto& ret_tuple =
 		    (obj->*work)(std::forward<Args>(args)...);
-		const SupplicantStatus& status = std::get<0>(ret_tuple);
+		const SupplicantStatusT& status = std::get<0>(ret_tuple);
 		const auto& ret_value1 = std::get<1>(ret_tuple);
 		const auto& ret_value2 = std::get<2>(ret_tuple);
 		hidl_cb(status, ret_value1, ret_value2);
diff --git a/wpa_supplicant/hidl/1.4/iface_config_utils.cpp b/wpa_supplicant/hidl/1.4/iface_config_utils.cpp
index b10523a..9af3ea9 100644
--- a/wpa_supplicant/hidl/1.4/iface_config_utils.cpp
+++ b/wpa_supplicant/hidl/1.4/iface_config_utils.cpp
@@ -84,6 +84,8 @@
 namespace V1_4 {
 namespace implementation {
 namespace iface_config_utils {
+using V1_0::SupplicantStatusCode;
+
 SupplicantStatus setWpsDeviceName(
     struct wpa_supplicant* wpa_s, const std::string& name)
 {
diff --git a/wpa_supplicant/hidl/1.4/p2p_iface.cpp b/wpa_supplicant/hidl/1.4/p2p_iface.cpp
index d3d374e..7baffa2 100644
--- a/wpa_supplicant/hidl/1.4/p2p_iface.cpp
+++ b/wpa_supplicant/hidl/1.4/p2p_iface.cpp
@@ -884,14 +884,14 @@
 Return<void> P2pIface::setEdmg(bool enable, setEdmg_cb _hidl_cb)
 {
 	return validateAndCall(
-	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
 	    &P2pIface::setEdmgInternal, _hidl_cb, enable);
 }
 
 Return<void> P2pIface::getEdmg(getEdmg_cb _hidl_cb)
 {
 	return validateAndCall(
-	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
 	    &P2pIface::getEdmgInternal, _hidl_cb);
 }
 
@@ -1796,14 +1796,6 @@
 	bool currentEnabledState = !!wpa_s->conf->p2p_device_random_mac_addr;
 	u8 *addr = NULL;
 
-	// A dedicated p2p device is not managed by supplicant,
-	// supplicant could not change its MAC address.
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) {
-		wpa_printf(MSG_ERROR,
-			"Dedicated P2P device don't support MAC randomization");
-		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, "NotSupported"};
-	}
-
 	// The same state, no change is needed.
 	if (currentEnabledState == enable) {
 		wpa_printf(MSG_DEBUG, "The random MAC is %s already.",
@@ -1847,19 +1839,19 @@
 	return {SupplicantStatusCode::SUCCESS, ""};
 }
 
-SupplicantStatus P2pIface::setEdmgInternal(bool enable)
+V1_4::SupplicantStatus P2pIface::setEdmgInternal(bool enable)
 {
 	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
 	wpa_printf(MSG_DEBUG, "set p2p_go_edmg to %d", enable);
 	wpa_s->conf->p2p_go_edmg = enable ? 1 : 0;
 	wpa_s->p2p_go_edmg = enable ? 1 : 0;
-	return {SupplicantStatusCode::SUCCESS, ""};
+	return {V1_4::SupplicantStatusCode::SUCCESS, ""};
 }
 
-std::pair<SupplicantStatus, bool> P2pIface::getEdmgInternal()
+std::pair<V1_4::SupplicantStatus, bool> P2pIface::getEdmgInternal()
 {
 	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
-	return {{SupplicantStatusCode::SUCCESS, ""},
+	return {{V1_4::SupplicantStatusCode::SUCCESS, ""},
 		(wpa_s->p2p_go_edmg == 1)};
 }
 
diff --git a/wpa_supplicant/hidl/1.4/p2p_iface.h b/wpa_supplicant/hidl/1.4/p2p_iface.h
index 4d07051..1694efe 100644
--- a/wpa_supplicant/hidl/1.4/p2p_iface.h
+++ b/wpa_supplicant/hidl/1.4/p2p_iface.h
@@ -30,6 +30,8 @@
 #include "config.h"
 }
 
+#define P2P_MGMT_DEVICE_PREFIX       "p2p-dev-"
+
 namespace android {
 namespace hardware {
 namespace wifi {
@@ -307,8 +309,8 @@
 	    bool persistent, uint32_t freq, const std::array<uint8_t, 6>& peer_address,
 	    bool joinExistingGroup);
 	SupplicantStatus setMacRandomizationInternal(bool enable);
-	SupplicantStatus setEdmgInternal(bool enable);
-	std::pair<SupplicantStatus, bool> getEdmgInternal();
+	V1_4::SupplicantStatus setEdmgInternal(bool enable);
+	std::pair<V1_4::SupplicantStatus, bool> getEdmgInternal();
 
 	struct wpa_supplicant* retrieveIfacePtr();
 	struct wpa_supplicant* retrieveGroupIfacePtr(
diff --git a/wpa_supplicant/hidl/1.4/sta_iface.cpp b/wpa_supplicant/hidl/1.4/sta_iface.cpp
index 7f476b1..40ec870 100644
--- a/wpa_supplicant/hidl/1.4/sta_iface.cpp
+++ b/wpa_supplicant/hidl/1.4/sta_iface.cpp
@@ -228,6 +228,7 @@
 namespace implementation {
 using hidl_return_util::validateAndCall;
 using V1_0::ISupplicantStaIfaceCallback;
+using V1_0::SupplicantStatusCode;
 
 StaIface::StaIface(struct wpa_global *wpa_global, const char ifname[])
     : wpa_global_(wpa_global), ifname_(ifname), is_valid_(true)
@@ -338,6 +339,16 @@
 	    &StaIface::registerCallbackInternal, _hidl_cb, callback_1_3);
 }
 
+Return<void> StaIface::registerCallback_1_4(
+    const sp<V1_4::ISupplicantStaIfaceCallback> &callback,
+    registerCallback_1_4_cb _hidl_cb)
+{
+	sp<V1_4::ISupplicantStaIfaceCallback> callback_1_4 = callback;
+	return validateAndCall(
+	    this, V1_4::SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::registerCallbackInternal_1_4, _hidl_cb, callback_1_4);
+}
+
 Return<void> StaIface::reassociate(reassociate_cb _hidl_cb)
 {
 	return validateAndCall(
@@ -770,7 +781,7 @@
     getConnectionCapabilities_1_4_cb _hidl_cb)
 {
 	return validateAndCall(
-	    this, SupplicantStatusCode::FAILURE_UNKNOWN,
+	    this, V1_4::SupplicantStatusCode::FAILURE_UNKNOWN,
 	    &StaIface::getConnectionCapabilitiesInternal_1_4, _hidl_cb);
 }
 
@@ -821,12 +832,18 @@
 SupplicantStatus StaIface::registerCallbackInternal(
     const sp<ISupplicantStaIfaceCallback> &callback)
 {
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
+}
+
+V1_4::SupplicantStatus StaIface::registerCallbackInternal_1_4(
+    const sp<V1_4::ISupplicantStaIfaceCallback> &callback)
+{
 	HidlManager *hidl_manager = HidlManager::getInstance();
 	if (!hidl_manager ||
 	    hidl_manager->addStaIfaceCallbackHidlObject(ifname_, callback)) {
-		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+		return {V1_4::SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
-	return {SupplicantStatusCode::SUCCESS, ""};
+	return {V1_4::SupplicantStatusCode::SUCCESS, ""};
 }
 
 SupplicantStatus StaIface::reassociateInternal()
@@ -952,9 +969,10 @@
 		    static_cast<std::underlying_type<
 			ISupplicantStaIface::Hs20AnqpSubtypes>::type>(type));
 	}
+
 	if (anqp_send_req(
-		wpa_s, mac_address.data(), info_elems_buf, num_info_elems,
-		sub_types_bitmask, false)) {
+		wpa_s, mac_address.data(), 0, info_elems_buf, num_info_elems,
+		sub_types_bitmask, 0)) {
 		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
 	return {SupplicantStatusCode::SUCCESS, ""};
@@ -1437,7 +1455,7 @@
 	return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, capa};
 }
 
-std::pair<SupplicantStatus, ConnectionCapabilities>
+std::pair<V1_4::SupplicantStatus, ConnectionCapabilities>
 StaIface::getConnectionCapabilitiesInternal_1_4()
 {
 	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
@@ -1489,7 +1507,7 @@
 		capa.V1_3.maxNumberRxSpatialStreams = 1;
 		capa.legacyMode = LegacyMode::UNKNOWN;
 	}
-	return {{SupplicantStatusCode::SUCCESS, ""}, capa};
+	return {{V1_4::SupplicantStatusCode::SUCCESS, ""}, capa};
 }
 
 std::pair<SupplicantStatus, uint32_t>
diff --git a/wpa_supplicant/hidl/1.4/sta_iface.h b/wpa_supplicant/hidl/1.4/sta_iface.h
index 1b8584d..b6590d3 100644
--- a/wpa_supplicant/hidl/1.4/sta_iface.h
+++ b/wpa_supplicant/hidl/1.4/sta_iface.h
@@ -16,7 +16,7 @@
 #include <android-base/macros.h>
 
 #include <android/hardware/wifi/supplicant/1.4/ISupplicantStaIface.h>
-#include <android/hardware/wifi/supplicant/1.2/ISupplicantStaIfaceCallback.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.h>
 #include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h>
 
 extern "C"
@@ -90,6 +90,9 @@
 	Return<void> registerCallback_1_3(
 	    const sp<V1_3::ISupplicantStaIfaceCallback>& callback,
 	    registerCallback_cb _hidl_cb) override;
+	Return<void> registerCallback_1_4(
+	    const sp<V1_4::ISupplicantStaIfaceCallback> &callback,
+	    registerCallback_1_4_cb _hidl_cb) override;
 	Return<void> reassociate(reassociate_cb _hidl_cb) override;
 	Return<void> reconnect(reconnect_cb _hidl_cb) override;
 	Return<void> disconnect(disconnect_cb _hidl_cb) override;
@@ -217,6 +220,8 @@
 	    const sp<V1_0::ISupplicantStaIfaceCallback>& callback);
 	SupplicantStatus registerCallbackInternal_1_1(
 	    const sp<V1_1::ISupplicantStaIfaceCallback>& callback);
+	V1_4::SupplicantStatus registerCallbackInternal_1_4(
+	    const sp<V1_4::ISupplicantStaIfaceCallback>& callback);
 	SupplicantStatus reassociateInternal();
 	SupplicantStatus reconnectInternal();
 	SupplicantStatus disconnectInternal();
@@ -285,7 +290,7 @@
 			uint32_t own_bootstrap_id);
 	SupplicantStatus stopDppInitiatorInternal();
 	std::pair<SupplicantStatus, V1_3::ConnectionCapabilities> getConnectionCapabilitiesInternal();
-	std::pair<SupplicantStatus, V1_4::ConnectionCapabilities>
+	std::pair<V1_4::SupplicantStatus, V1_4::ConnectionCapabilities>
 			getConnectionCapabilitiesInternal_1_4();
 	std::pair<SupplicantStatus, uint32_t> getWpaDriverCapabilitiesInternal();
 	SupplicantStatus setMboCellularDataStatusInternal(bool available);
diff --git a/wpa_supplicant/hidl/1.4/sta_network.cpp b/wpa_supplicant/hidl/1.4/sta_network.cpp
index a87c9eb..d62fdae 100644
--- a/wpa_supplicant/hidl/1.4/sta_network.cpp
+++ b/wpa_supplicant/hidl/1.4/sta_network.cpp
@@ -96,6 +96,9 @@
 constexpr char kNetworkEapSimUmtsAutsResponse[] = "UMTS-AUTS";
 constexpr char kNetworkEapSimGsmAuthFailure[] = "GSM-FAIL";
 constexpr char kNetworkEapSimUmtsAuthFailure[] = "UMTS-FAIL";
+/* These constants refer definitions in wpa_supplicant/config.h */
+constexpr int SAE_PWE_H2E_ONLY_MODE = 1;
+constexpr int SAE_PWE_HP_H2E_BOTH = 2;
 
 #ifdef CONFIG_WAPI_INTERFACE
 std::string dummyWapiCertSuite;
@@ -110,6 +113,7 @@
 namespace V1_4 {
 namespace implementation {
 using hidl_return_util::validateAndCall;
+using V1_0::SupplicantStatus;
 using V1_0::SupplicantStatusCode;
 
 StaNetwork::StaNetwork(
@@ -156,6 +160,15 @@
 	    &StaNetwork::registerCallbackInternal, _hidl_cb, callback);
 }
 
+Return<void> StaNetwork::registerCallback_1_4(
+    const sp<V1_4::ISupplicantStaNetworkCallback> &callback,
+    registerCallback_1_4_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::registerCallback_1_4Internal, _hidl_cb, callback);
+}
+
 Return<void> StaNetwork::setSsid(
     const hidl_vec<uint8_t> &ssid, setSsid_cb _hidl_cb)
 {
@@ -415,7 +428,7 @@
 Return<void> StaNetwork::setEdmg(bool enable, setEdmg_cb _hidl_cb)
 {
 	return validateAndCall(
-	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
 	    &StaNetwork::setEdmgInternal, _hidl_cb, enable);
 }
 
@@ -650,7 +663,7 @@
 Return<void> StaNetwork::getEdmg(getEdmg_cb _hidl_cb)
 {
 	return validateAndCall(
-	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
 	    &StaNetwork::getEdmgInternal, _hidl_cb);
 }
 
@@ -949,14 +962,14 @@
     uint32_t group_cipher_mask, setGroupCipher_1_4_cb _hidl_cb)
 {
 	return validateAndCall(
-	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
 	    &StaNetwork::setGroupCipher_1_4Internal, _hidl_cb, group_cipher_mask);
 }
 
 Return<void> StaNetwork::getGroupCipher_1_4(getGroupCipher_1_4_cb _hidl_cb)
 {
 	return validateAndCall(
-	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
 	    &StaNetwork::getGroupCipher_1_4Internal, _hidl_cb);
 }
 
@@ -964,7 +977,7 @@
     uint32_t pairwise_cipher_mask, setPairwiseCipher_1_4_cb _hidl_cb)
 {
 	return validateAndCall(
-	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
 	    &StaNetwork::setPairwiseCipher_1_4Internal, _hidl_cb,
 	    pairwise_cipher_mask);
 }
@@ -973,10 +986,17 @@
     getPairwiseCipher_1_4_cb _hidl_cb)
 {
 	return validateAndCall(
-	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
 	    &StaNetwork::getPairwiseCipher_1_4Internal, _hidl_cb);
 }
 
+Return<void> StaNetwork::enableSaeH2eOnlyMode(bool enable, enableSaeH2eOnlyMode_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::enableSaeH2eOnlyModeInternal, _hidl_cb, enable);
+}
+
 std::pair<SupplicantStatus, uint32_t> StaNetwork::getIdInternal()
 {
 	return {{SupplicantStatusCode::SUCCESS, ""}, network_id_};
@@ -995,12 +1015,18 @@
 SupplicantStatus StaNetwork::registerCallbackInternal(
     const sp<ISupplicantStaNetworkCallback> &callback)
 {
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
+}
+
+V1_4::SupplicantStatus StaNetwork::registerCallback_1_4Internal(
+    const sp<V1_4::ISupplicantStaNetworkCallback> &callback)
+{
 	HidlManager *hidl_manager = HidlManager::getInstance();
 	if (!hidl_manager || hidl_manager->addStaNetworkCallbackHidlObject(
 				 ifname_, network_id_, callback)) {
-		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+		return {V1_4::SupplicantStatusCode::FAILURE_UNKNOWN, ""};
 	}
-	return {SupplicantStatusCode::SUCCESS, ""};
+	return {V1_4::SupplicantStatusCode::SUCCESS, ""};
 }
 
 SupplicantStatus StaNetwork::setSsidInternal(const std::vector<uint8_t> &ssid)
@@ -1077,12 +1103,12 @@
 	return {SupplicantStatusCode::SUCCESS, ""};
 }
 
-SupplicantStatus StaNetwork::setEdmgInternal(bool enable)
+V1_4::SupplicantStatus StaNetwork::setEdmgInternal(bool enable)
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	wpa_ssid->enable_edmg = enable ? 1 : 0;
 	resetInternalStateAfterParamsUpdate();
-	return {SupplicantStatusCode::SUCCESS, ""};
+	return {V1_4::SupplicantStatusCode::SUCCESS, ""};
 }
 
 SupplicantStatus StaNetwork::setGroupCipherInternal(uint32_t group_cipher_mask)
@@ -1851,10 +1877,10 @@
 	return {{SupplicantStatusCode::SUCCESS, ""}, {wpa_ssid->id_str}};
 }
 
-std::pair<SupplicantStatus, bool> StaNetwork::getEdmgInternal()
+std::pair<V1_4::SupplicantStatus, bool> StaNetwork::getEdmgInternal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	return {{SupplicantStatusCode::SUCCESS, ""},
+	return {{V1_4::SupplicantStatusCode::SUCCESS, ""},
 		(wpa_ssid->enable_edmg == 1)};
 }
 
@@ -2295,43 +2321,43 @@
 	return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, 0};
 }
 
-SupplicantStatus StaNetwork::setGroupCipher_1_4Internal(uint32_t group_cipher_mask)
+V1_4::SupplicantStatus StaNetwork::setGroupCipher_1_4Internal(uint32_t group_cipher_mask)
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (group_cipher_mask & ~kAllowedGroupCipherMask) {
-		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+		return {V1_4::SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
 	}
 	wpa_ssid->group_cipher = group_cipher_mask;
 	wpa_printf(MSG_MSGDUMP, "group_cipher: 0x%x", wpa_ssid->group_cipher);
 	resetInternalStateAfterParamsUpdate();
-	return {SupplicantStatusCode::SUCCESS, ""};
+	return {V1_4::SupplicantStatusCode::SUCCESS, ""};
 }
 
-std::pair<SupplicantStatus, uint32_t> StaNetwork::getGroupCipher_1_4Internal()
+std::pair<V1_4::SupplicantStatus, uint32_t> StaNetwork::getGroupCipher_1_4Internal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	return {{SupplicantStatusCode::SUCCESS, ""},
+	return {{V1_4::SupplicantStatusCode::SUCCESS, ""},
 		wpa_ssid->group_cipher & kAllowedGroupCipherMask};
 }
 
-SupplicantStatus StaNetwork::setPairwiseCipher_1_4Internal(
+V1_4::SupplicantStatus StaNetwork::setPairwiseCipher_1_4Internal(
     uint32_t pairwise_cipher_mask)
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
 	if (pairwise_cipher_mask & ~kAllowedPairwisewCipherMask) {
-		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+		return {V1_4::SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
 	}
 	wpa_ssid->pairwise_cipher = pairwise_cipher_mask;
 	wpa_printf(
 	    MSG_MSGDUMP, "pairwise_cipher: 0x%x", wpa_ssid->pairwise_cipher);
 	resetInternalStateAfterParamsUpdate();
-	return {SupplicantStatusCode::SUCCESS, ""};
+	return {V1_4::SupplicantStatusCode::SUCCESS, ""};
 }
 
-std::pair<SupplicantStatus, uint32_t> StaNetwork::getPairwiseCipher_1_4Internal()
+std::pair<V1_4::SupplicantStatus, uint32_t> StaNetwork::getPairwiseCipher_1_4Internal()
 {
 	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
-	return {{SupplicantStatusCode::SUCCESS, ""},
+	return {{V1_4::SupplicantStatusCode::SUCCESS, ""},
 		wpa_ssid->pairwise_cipher & kAllowedPairwisewCipherMask};
 }
 
@@ -2582,6 +2608,14 @@
 #endif /* CONFIG_FILS */
 }
 
+V1_4::SupplicantStatus StaNetwork::enableSaeH2eOnlyModeInternal(bool enable)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	wpa_s->conf->sae_pwe = enable ? SAE_PWE_H2E_ONLY_MODE : SAE_PWE_HP_H2E_BOTH;
+	resetInternalStateAfterParamsUpdate();
+	return {V1_4::SupplicantStatusCode::SUCCESS, ""};
+}
+
 }  // namespace implementation
 }  // namespace V1_4
 }  // namespace supplicant
diff --git a/wpa_supplicant/hidl/1.4/sta_network.h b/wpa_supplicant/hidl/1.4/sta_network.h
index 83b47d0..b00f8e5 100644
--- a/wpa_supplicant/hidl/1.4/sta_network.h
+++ b/wpa_supplicant/hidl/1.4/sta_network.h
@@ -62,6 +62,9 @@
 	Return<void> registerCallback(
 	    const sp<ISupplicantStaNetworkCallback>& callback,
 	    registerCallback_cb _hidl_cb) override;
+	Return<void> registerCallback_1_4(
+	    const sp<V1_4::ISupplicantStaNetworkCallback>& callback,
+	    registerCallback_1_4_cb _hidl_cb) override;
 	Return<void> setSsid(
 	    const hidl_vec<uint8_t>& ssid, setSsid_cb _hidl_cb) override;
 	Return<void> setBssid(
@@ -268,6 +271,7 @@
 	    setGroupCipher_1_4_cb _hidl_cb) override;
 	Return<void> getGroupCipher_1_4(
 	    getGroupCipher_1_4_cb _hidl_cb) override;
+	Return<void> enableSaeH2eOnlyMode(bool enable, enableSaeH2eOnlyMode_cb _hidl_cb) override;
 
 private:
 	// Corresponding worker functions for the HIDL methods.
@@ -276,6 +280,8 @@
 	std::pair<SupplicantStatus, IfaceType> getTypeInternal();
 	SupplicantStatus registerCallbackInternal(
 	    const sp<ISupplicantStaNetworkCallback>& callback);
+	V1_4::SupplicantStatus registerCallback_1_4Internal(
+	    const sp<V1_4::ISupplicantStaNetworkCallback>& callback);
 	SupplicantStatus setSsidInternal(const std::vector<uint8_t>& ssid);
 	SupplicantStatus setBssidInternal(const std::array<uint8_t, 6>& bssid);
 	SupplicantStatus setScanSsidInternal(bool enable);
@@ -317,7 +323,7 @@
 	SupplicantStatus setProactiveKeyCachingInternal(bool enable);
 	SupplicantStatus setIdStrInternal(const std::string& id_str);
 	SupplicantStatus setUpdateIdentifierInternal(uint32_t id);
-	SupplicantStatus setEdmgInternal(bool enable);
+	V1_4::SupplicantStatus setEdmgInternal(bool enable);
 	std::pair<SupplicantStatus, std::vector<uint8_t>> getSsidInternal();
 	std::pair<SupplicantStatus, std::array<uint8_t, 6>> getBssidInternal();
 	std::pair<SupplicantStatus, bool> getScanSsidInternal();
@@ -358,7 +364,7 @@
 	std::pair<SupplicantStatus, std::string> getIdStrInternal();
 	std::pair<SupplicantStatus, std::vector<uint8_t>>
 	getWpsNfcConfigurationTokenInternal();
-	std::pair<SupplicantStatus, bool> getEdmgInternal();
+	std::pair<V1_4::SupplicantStatus, bool> getEdmgInternal();
 	SupplicantStatus enableInternal(bool no_connect);
 	SupplicantStatus disableInternal();
 	SupplicantStatus selectInternal();
@@ -402,11 +408,12 @@
 	    uint32_t pairwise_cipher_mask);
 	SupplicantStatus setWapiPskInternal(const std::vector<uint8_t>& psk);
 	std::pair<SupplicantStatus, std::vector<uint8_t>> getWapiPskInternal();
-	std::pair<SupplicantStatus, uint32_t> getGroupCipher_1_4Internal();
-	SupplicantStatus setGroupCipher_1_4Internal(uint32_t group_cipher_mask);
-	std::pair<SupplicantStatus, uint32_t> getPairwiseCipher_1_4Internal();
-	SupplicantStatus setPairwiseCipher_1_4Internal(
+	std::pair<V1_4::SupplicantStatus, uint32_t> getGroupCipher_1_4Internal();
+	V1_4::SupplicantStatus setGroupCipher_1_4Internal(uint32_t group_cipher_mask);
+	std::pair<V1_4::SupplicantStatus, uint32_t> getPairwiseCipher_1_4Internal();
+	V1_4::SupplicantStatus setPairwiseCipher_1_4Internal(
 	    uint32_t pairwise_cipher_mask);
+	V1_4::SupplicantStatus enableSaeH2eOnlyModeInternal(bool enable);
 
 	struct wpa_ssid* retrieveNetworkPtr();
 	struct wpa_supplicant* retrieveIfacePtr();
diff --git a/wpa_supplicant/hidl/1.4/supplicant.cpp b/wpa_supplicant/hidl/1.4/supplicant.cpp
index 5fea68d..6f4cde6 100644
--- a/wpa_supplicant/hidl/1.4/supplicant.cpp
+++ b/wpa_supplicant/hidl/1.4/supplicant.cpp
@@ -10,6 +10,7 @@
 #include "hidl_manager.h"
 #include "hidl_return_util.h"
 #include "supplicant.h"
+#include "p2p_iface.h"
 
 #include <android-base/file.h>
 #include <fcntl.h>
@@ -254,6 +255,39 @@
 }
 
 std::pair<SupplicantStatus, sp<ISupplicantIface>>
+Supplicant::addP2pDevInterface(struct wpa_interface iface_params)
+{
+	char primary_ifname[IFNAMSIZ];
+	u32 primary_ifname_len =
+		strlen(iface_params.ifname) - strlen(P2P_MGMT_DEVICE_PREFIX);
+
+	if(primary_ifname_len > IFNAMSIZ) {
+		wpa_printf(MSG_DEBUG, "%s, Invalid primary iface name ", __FUNCTION__);
+		return {{SupplicantStatusCode::FAILURE_ARGS_INVALID, ""}, {}};
+	}
+
+	strncpy(primary_ifname, iface_params.ifname +
+		strlen(P2P_MGMT_DEVICE_PREFIX), primary_ifname_len);
+	wpa_printf(MSG_DEBUG, "%s, Initialize p2p-dev-wlan0 iface with"
+		"primary_iface = %s", __FUNCTION__, primary_ifname);
+	struct wpa_supplicant* wpa_s =
+		wpa_supplicant_get_iface(wpa_global_, primary_ifname);
+	if (!wpa_s) {
+		wpa_printf(MSG_DEBUG, "%s,NULL wpa_s for wlan0", __FUNCTION__);
+		return {{SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""},
+			nullptr};
+	}
+	if (wpas_p2p_add_p2pdev_interface(
+		wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
+		wpa_printf(MSG_INFO,
+			"Failed to enable P2P Device");
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN,
+			"Enable P2P Device failed"}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS,""}, {}};
+}
+
+std::pair<SupplicantStatus, sp<ISupplicantIface>>
 Supplicant::addInterfaceInternal(const IfaceInfo& iface_info)
 {
 	android::sp<ISupplicantIface> iface;
@@ -305,10 +339,19 @@
 		}
 	}
 	iface_params.ifname = iface_info.name.c_str();
-	struct wpa_supplicant* wpa_s =
-	    wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL);
-	if (!wpa_s) {
-		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	if (strncmp(iface_params.ifname, P2P_MGMT_DEVICE_PREFIX,
+		strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
+		std::tie(status, iface) = addP2pDevInterface(iface_params);
+		if (status.code != SupplicantStatusCode::SUCCESS) {
+			return {{status.code,
+				status.debugMessage.c_str()}, iface};
+		}
+	} else {
+		struct wpa_supplicant* wpa_s =
+			wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL);
+		if (!wpa_s) {
+			return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+		}
 	}
 	// The supplicant core creates a corresponding hidl object via
 	// HidlManager when |wpa_supplicant_add_iface| is called.
diff --git a/wpa_supplicant/hidl/1.4/supplicant.h b/wpa_supplicant/hidl/1.4/supplicant.h
index 8fd2033..2944c50 100644
--- a/wpa_supplicant/hidl/1.4/supplicant.h
+++ b/wpa_supplicant/hidl/1.4/supplicant.h
@@ -81,6 +81,8 @@
 	SupplicantStatus setDebugParamsInternal(
 	    ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys);
 	SupplicantStatus setConcurrencyPriorityInternal(IfaceType type);
+	std::pair<SupplicantStatus, sp<ISupplicantIface>> addP2pDevInterface(
+	    struct wpa_interface iface_params);
 
 	// Raw pointer to the global structure maintained by the core.
 	struct wpa_global* wpa_global_;
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index ce5608e..907803b 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -1335,6 +1335,7 @@
 	}
 
 	wpa_msg(wpa_s, MSG_INFO, HS20_T_C_ACCEPTANCE "%s", url);
+	wpas_notify_hs20_rx_terms_and_conditions_acceptance(wpa_s, url);
 }
 
 
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index b60f80d..c799b5a 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -959,7 +959,9 @@
 			"WPA-EAP WPA-EAP-SHA256" : "WPA-EAP";
 	if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0 ||
 	    wpa_config_set(ssid, "proto", "RSN", 0) < 0 ||
-	    wpa_config_set(ssid, "ieee80211w", "1", 0) < 0 ||
+	    wpa_config_set(ssid, "ieee80211w",
+			   wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_REQUIRED ?
+			   "2" : "1", 0) < 0 ||
 	    wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
 		return -1;
 	return 0;
@@ -2748,27 +2750,27 @@
 }
 
 
-int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, int freq,
 		  u16 info_ids[], size_t num_ids, u32 subtypes,
 		  u32 mbo_subtypes)
 {
 	struct wpabuf *buf;
 	struct wpabuf *extra_buf = NULL;
 	int ret = 0;
-	int freq;
 	struct wpa_bss *bss;
 	int res;
 
 	bss = wpa_bss_get_bssid(wpa_s, dst);
-	if (!bss) {
+	if (!bss && !freq) {
 		wpa_printf(MSG_WARNING,
-			   "ANQP: Cannot send query to unknown BSS "
-			   MACSTR, MAC2STR(dst));
+			   "ANQP: Cannot send query without BSS freq info");
 		return -1;
 	}
 
-	wpa_bss_anqp_unshare_alloc(bss);
-	freq = bss->freq;
+	if (bss)
+		wpa_bss_anqp_unshare_alloc(bss);
+	if (bss && !freq)
+		freq = bss->freq;
 
 	wpa_msg(wpa_s, MSG_DEBUG,
 		"ANQP: Query Request to " MACSTR " for %u id(s)",
@@ -2787,6 +2789,13 @@
 	if (mbo_subtypes) {
 		struct wpabuf *mbo;
 
+		if (!bss) {
+			wpa_printf(MSG_WARNING,
+				   "ANQP: Cannot send MBO query to unknown BSS "
+				   MACSTR, MAC2STR(dst));
+			return -1;
+		}
+
 		mbo = mbo_build_anqp_buf(wpa_s, bss, mbo_subtypes);
 		if (mbo) {
 			if (wpabuf_resize(&extra_buf, wpabuf_len(mbo))) {
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 37ee2e9..77b2c91 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -11,7 +11,7 @@
 
 enum gas_query_result;
 
-int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, int freq,
 		  u16 info_ids[], size_t num_ids, u32 subtypes,
 		  u32 mbo_subtypes);
 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index c085466..558d87a 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -333,30 +333,6 @@
 			   frequency);
 		goto out_free;
 	}
-	if (ssid->ht40)
-		conf->secondary_channel = ssid->ht40;
-	if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && ssid->vht) {
-		if (ssid->max_oper_chwidth != DEFAULT_MAX_OPER_CHWIDTH)
-			conf->vht_oper_chwidth = ssid->max_oper_chwidth;
-		switch (conf->vht_oper_chwidth) {
-		case CHANWIDTH_80MHZ:
-		case CHANWIDTH_80P80MHZ:
-			ieee80211_freq_to_chan(
-				frequency,
-				&conf->vht_oper_centr_freq_seg0_idx);
-			conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
-			break;
-		case CHANWIDTH_160MHZ:
-			ieee80211_freq_to_chan(
-				frequency,
-				&conf->vht_oper_centr_freq_seg0_idx);
-			conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
-			conf->vht_oper_centr_freq_seg0_idx += 40 / 5;
-			break;
-		}
-		ieee80211_freq_to_chan(ssid->vht_center_freq2,
-				       &conf->vht_oper_centr_freq_seg1_idx);
-	}
 
 	if (ssid->mesh_basic_rates == NULL) {
 		/*
@@ -387,6 +363,31 @@
 		conf->basic_rates[rate_len] = -1;
 	}
 
+	/* While it can enhance performance to switch the primary channel, which
+	 * is also the secondary channel of another network at the same time),
+	 * to the other primary channel, problems exist with this in mesh
+	 * networks.
+	 *
+	 * Example with problems:
+	 *     - 3 mesh nodes M1-M3, freq (5200, 5180)
+	 *     - other node O1, e.g. AP mode, freq (5180, 5200),
+	 * Locations: O1 M1      M2      M3
+	 *
+	 * M3 can only send frames to M1 over M2, no direct connection is
+	 * possible
+	 * Start O1, M1 and M3 first, M1 or O1 will switch channels to align
+	 * with* each other. M3 does not swap, because M1 or O1 cannot be
+	 * reached. M2 is started afterwards and can either connect to M3 or M1
+	 * because of this primary secondary channel switch.
+	 *
+	 * Solutions: (1) central coordination -> not always possible
+	 *            (2) disable pri/sec channel switch in mesh networks
+	 *
+	 * In AP mode, when all nodes can work independently, this poses of
+	 * course no problem, therefore disable it only in mesh mode. */
+	conf->no_pri_sec_switch = 1;
+	wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
+
 	if (wpa_drv_init_mesh(wpa_s)) {
 		wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver");
 		return -1;
@@ -398,8 +399,6 @@
 		return -1;
 	}
 
-	wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
-
 	return 0;
 out_free:
 	wpa_supplicant_mesh_deinit(wpa_s);
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 12aafcb..b6a5e88 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -533,11 +533,14 @@
 	int reason = WLAN_REASON_MESH_PEERING_CANCELLED;
 
 	if (sta) {
+		if (sta->plink_state == PLINK_ESTAB)
+			hapd->num_plinks--;
 		wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
 		mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
 		wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR,
 			   MAC2STR(sta->addr));
 		eloop_cancel_timeout(plink_timer, wpa_s, sta);
+		eloop_cancel_timeout(mesh_auth_timer, wpa_s, sta);
 		return 0;
 	}
 
@@ -1290,8 +1293,8 @@
 
 			if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
 						 tx_chanwidth, tx_seg1_idx) !=
-			    0) {
-				wpa_printf(MSG_WARNING, "MPM: %s",
+			    OCI_SUCCESS) {
+				wpa_printf(MSG_WARNING, "MPM: OCV failed: %s",
 					   ocv_errorstr);
 				return;
 			}
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index f19bfbf..834c7a1 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -195,7 +195,8 @@
 		wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK",
 				rsn->igtk, rsn->igtk_len);
 		wpa_drv_set_key(rsn->wpa_s,
-				wpa_cipher_to_alg(rsn->mgmt_group_cipher), NULL,
+				wpa_cipher_to_alg(rsn->mgmt_group_cipher),
+				broadcast_ether_addr,
 				rsn->igtk_key_id, 1,
 				seq, sizeof(seq), rsn->igtk, rsn->igtk_len,
 				KEY_FLAG_GROUP_TX_DEFAULT);
@@ -204,7 +205,8 @@
 	/* group privacy / data frames */
 	wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
 			rsn->mgtk, rsn->mgtk_len);
-	wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher), NULL,
+	wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher),
+			broadcast_ether_addr,
 			rsn->mgtk_key_id, 1, seq, sizeof(seq),
 			rsn->mgtk, rsn->mgtk_len, KEY_FLAG_GROUP_TX_DEFAULT);
 
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 16e747f..ac40074 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -1007,14 +1007,23 @@
 						const char *url)
 {
 #ifdef CONFIG_HS20
-	if (!wpa_s || !url)
+	if (!wpa_s)
 		return;
 
 	wpas_hidl_notify_hs20_rx_deauth_imminent_notice(wpa_s, code, reauth_delay,
-							url);
+			url);
 #endif /* CONFIG_HS20 */
 }
 
+void wpas_notify_hs20_rx_terms_and_conditions_acceptance(
+		struct wpa_supplicant *wpa_s, const char *url) {
+#ifdef CONFIG_HS20
+	if (!wpa_s || !url)
+		return;
+
+	wpas_hidl_notify_hs20_rx_terms_and_conditions_acceptance(wpa_s, url);
+#endif /* CONFIG_HS20 */
+}
 
 #ifdef CONFIG_MESH
 
@@ -1208,3 +1217,16 @@
 
 	wpas_hidl_notify_pmk_cache_added(wpa_s, entry);
 }
+
+void wpas_notify_transition_disable(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid,
+				    u8 bitmap)
+{
+	if (!wpa_s)
+		return;
+
+	if (!ssid)
+		return;
+
+	wpas_hidl_notify_transition_disable(wpa_s, ssid, bitmap);
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 0e7991b..d75bc77 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -175,6 +175,8 @@
 void wpas_notify_hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s,
 						u8 code, u16 reauth_delay,
 						const char *url);
+void wpas_notify_hs20_rx_terms_and_conditions_acceptance(
+		struct wpa_supplicant *wpa_s, const char *url);
 void wpas_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
 	    struct wpa_ssid *ssid);
 void wpas_notify_dpp_config_sent(struct wpa_supplicant *wpa_s);
@@ -194,5 +196,8 @@
 void wpas_notify_dpp_config_rejected(struct wpa_supplicant *wpa_s);
 void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s,
 				 struct rsn_pmksa_cache_entry *entry);
+void wpas_notify_transition_disable(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid,
+				    u8 bitmap);
 
 #endif /* NOTIFY_H */
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
index bd97fee..91e15e7 100644
--- a/wpa_supplicant/op_classes.c
+++ b/wpa_supplicant/op_classes.c
@@ -412,9 +412,13 @@
 	}
 
 	*ie_len = wpabuf_len(buf) - 2;
-	if (*ie_len < 2 || wpabuf_len(buf) > len) {
+	if (*ie_len < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "No supported operating classes IE to add");
+		res = 0;
+	} else if (wpabuf_len(buf) > len) {
 		wpa_printf(MSG_ERROR,
-			   "Failed to add supported operating classes IE");
+			   "Supported operating classes IE exceeds maximum buffer length");
 		res = 0;
 	} else {
 		os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf));
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index eb7c392..dc5c16d 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -291,6 +291,41 @@
 }
 
 
+static int wpas_p2p_add_scan_freq_list(struct wpa_supplicant *wpa_s,
+				       enum hostapd_hw_mode band,
+				       struct wpa_driver_scan_params *params)
+{
+	struct hostapd_hw_modes *mode;
+	int num_chans = 0;
+	int *freqs, i;
+
+	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band, 0);
+	if (!mode)
+		return -1;
+
+	if (params->freqs) {
+		while (params->freqs[num_chans])
+			num_chans++;
+	}
+
+	freqs = os_realloc(params->freqs,
+			   (num_chans + mode->num_channels + 1) * sizeof(int));
+	if (!freqs)
+		return -1;
+
+	params->freqs = freqs;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		params->freqs[num_chans++] = mode->channels[i].freq;
+	}
+	params->freqs[num_chans] = 0;
+
+	return 0;
+}
+
+
 static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
 {
 	struct wpa_supplicant *wpa_s = work->wpa_s;
@@ -312,6 +347,15 @@
 			   "Request driver to clear scan cache due to local BSS flush");
 		params->only_new_results = 1;
 	}
+
+	if (wpa_s->conf->p2p_6ghz_disable && !params->freqs) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: 6 GHz disabled - update the scan frequency list");
+		wpas_p2p_add_scan_freq_list(wpa_s, HOSTAPD_MODE_IEEE80211G,
+					    params);
+		wpas_p2p_add_scan_freq_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+					    params);
+	}
 	ret = wpa_drv_scan(wpa_s, params);
 	if (ret == 0)
 		wpa_s->curr_scan_cookie = params->scan_cookie;
@@ -522,7 +566,7 @@
 		/*
 		 * The calling wpa_s instance is going to be removed. Do that
 		 * from an eloop callback to keep the instance available until
-		 * the caller has returned. This my be needed, e.g., to provide
+		 * the caller has returned. This may be needed, e.g., to provide
 		 * control interface responses on the per-interface socket.
 		 */
 		if (eloop_register_timeout(0, 0, run_wpas_p2p_disconnect,
@@ -3736,7 +3780,9 @@
 		u8 ch;
 		struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
 
-		if (o->p2p == NO_P2P_SUPP)
+		if (o->p2p == NO_P2P_SUPP ||
+		    (is_6ghz_op_class(o->op_class) &&
+		     wpa_s->conf->p2p_6ghz_disable))
 			continue;
 
 		mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode,
@@ -3747,6 +3793,13 @@
 			wpa_s->global->p2p_24ghz_social_channels = 1;
 		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
 			enum chan_allowed res;
+
+			/* Check for non-continuous jump in channel index
+			 * incrementation */
+			if ((o->op_class == 128 || o->op_class == 130) &&
+			    ch < 149 && ch + o->inc > 149)
+				ch = 149;
+
 			res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
 			if (res == ALLOWED) {
 				if (reg == NULL) {
@@ -3807,7 +3860,9 @@
 		const struct oper_class_map *o = &global_op_class[op];
 		u8 ch;
 
-		if (o->p2p == NO_P2P_SUPP)
+		if (o->p2p == NO_P2P_SUPP ||
+		    (is_6ghz_op_class(o->op_class) &&
+		     wpa_s->conf->p2p_6ghz_disable))
 			continue;
 
 		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
@@ -3939,6 +3994,10 @@
 			  wpa_s->ifname);
 	if (os_snprintf_error(sizeof(ifname), ret))
 		return -1;
+	/* Cut length at the maximum size. Note that we don't need to ensure
+	 * collision free names here as the created interface is not a netdev.
+	 */
+	ifname[IFNAMSIZ - 1] = '\0';
 	force_name[0] = '\0';
 	wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE;
 	ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, NULL, NULL,
@@ -4777,6 +4836,7 @@
 	eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 	wpas_p2p_remove_pending_group_interface(wpa_s);
 	eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
 	wpas_p2p_listen_work_done(wpa_s);
 	if (wpa_s->p2p_send_action_work) {
 		os_free(wpa_s->p2p_send_action_work->ctx);
@@ -5284,6 +5344,13 @@
 	if (freq > 0) {
 		freqs[0] = freq;
 		params.freqs = freqs;
+	} else if (wpa_s->conf->p2p_6ghz_disable) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: 6 GHz disabled - update the scan frequency list");
+		wpas_p2p_add_scan_freq_list(wpa_s, HOSTAPD_MODE_IEEE80211G,
+					    &params);
+		wpas_p2p_add_scan_freq_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+					    &params);
 	}
 
 	ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
@@ -5314,6 +5381,8 @@
 	 * the new scan results become available.
 	 */
 	ret = wpa_drv_scan(wpa_s, &params);
+	if (wpa_s->conf->p2p_6ghz_disable && params.freqs != freqs)
+		os_free(params.freqs);
 	if (!ret) {
 		os_get_reltime(&wpa_s->scan_trigger_time);
 		wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
@@ -5654,6 +5723,9 @@
 			return -1;
 	}
 
+	if (is_6ghz_freq(freq) && wpa_s->conf->p2p_6ghz_disable)
+		return -2;
+
 	os_free(wpa_s->global->add_psk);
 	wpa_s->global->add_psk = NULL;
 
@@ -5880,6 +5952,8 @@
 
 	if (os_strcmp(ifname, "*") == 0) {
 		struct wpa_supplicant *prev;
+		bool calling_wpa_s_group_removed = false;
+
 		wpa_s = global->ifaces;
 		while (wpa_s) {
 			prev = wpa_s;
@@ -5887,9 +5961,23 @@
 			if (prev->p2p_group_interface !=
 			    NOT_P2P_GROUP_INTERFACE ||
 			    (prev->current_ssid &&
-			     prev->current_ssid->p2p_group))
+			     prev->current_ssid->p2p_group)) {
 				wpas_p2p_disconnect_safely(prev, calling_wpa_s);
+				if (prev == calling_wpa_s)
+					calling_wpa_s_group_removed = true;
+			}
 		}
+
+		if (!calling_wpa_s_group_removed &&
+		    (calling_wpa_s->p2p_group_interface !=
+		     NOT_P2P_GROUP_INTERFACE ||
+		     (calling_wpa_s->current_ssid &&
+		      calling_wpa_s->current_ssid->p2p_group))) {
+			wpa_printf(MSG_DEBUG, "Remove calling_wpa_s P2P group");
+			wpas_p2p_disconnect_safely(calling_wpa_s,
+						   calling_wpa_s);
+		}
+
 		return 0;
 	}
 
@@ -9406,6 +9494,8 @@
 {
 	struct p2p_go_neg_results params;
 	struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+	void (*ap_configured_cb)(void *ctx, void *data);
+	void *ap_configured_cb_ctx, *ap_configured_cb_data;
 
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
 
@@ -9415,12 +9505,13 @@
 	/* Stop the AP functionality */
 	/* TODO: Should do this in a way that does not indicated to possible
 	 * P2P Clients in the group that the group is terminated. */
-	/* If this action occurs before a group is started, the callback should be
-	 * preserved, or GROUP-STARTED event would be lost. If this action occurs after
-	 * a group is started, these poiners are all NULL and harmless. */
-	void (*ap_configured_cb)(void *ctx, void *data) = wpa_s->ap_configured_cb;
-	void *ap_configured_cb_ctx = wpa_s->ap_configured_cb_ctx;;
-	void *ap_configured_cb_data = wpa_s->ap_configured_cb_data;
+	/* If this action occurs before a group is started, the callback should
+	 * be preserved, or GROUP-STARTED event would be lost. If this action
+	 * occurs after a group is started, these pointers are all NULL and
+	 * harmless. */
+	ap_configured_cb = wpa_s->ap_configured_cb;
+	ap_configured_cb_ctx = wpa_s->ap_configured_cb_ctx;
+	ap_configured_cb_data = wpa_s->ap_configured_cb_data;
 	wpa_supplicant_ap_deinit(wpa_s);
 
 	/* Reselect the GO frequency */
@@ -9444,9 +9535,11 @@
 		return;
 	}
 
+	/* Restore preserved callback parameters */
 	wpa_s->ap_configured_cb = ap_configured_cb;
 	wpa_s->ap_configured_cb_ctx = ap_configured_cb_ctx;
 	wpa_s->ap_configured_cb_data = ap_configured_cb_data;
+
 	/* Update the frequency */
 	current_ssid->frequency = params.freq;
 	wpa_s->connect_without_scan = current_ssid;
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 4a8f4ff..de49948 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -154,7 +154,8 @@
 				    const u8 *bssid, const u8 *pmkid,
 				    const u8 *fils_cache_id,
 				    const u8 *pmk, size_t pmk_len,
-				    u32 pmk_lifetime, u8 pmk_reauth_threshold)
+				    u32 pmk_lifetime, u8 pmk_reauth_threshold,
+				    int akmp)
 {
 	printf("%s - not implemented\n", __func__);
 	return -1;
diff --git a/wpa_supplicant/robust_av.c b/wpa_supplicant/robust_av.c
new file mode 100644
index 0000000..1280f5d
--- /dev/null
+++ b/wpa_supplicant/robust_av.c
@@ -0,0 +1,160 @@
+/*
+ * wpa_supplicant - Robust AV procedures
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "bss.h"
+
+
+void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
+				      struct wpabuf *buf)
+{
+	u8 *len, *len1;
+
+	/* MSCS descriptor element */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	len = wpabuf_put(buf, 1);
+	wpabuf_put_u8(buf, WLAN_EID_EXT_MSCS_DESCRIPTOR);
+	wpabuf_put_u8(buf, robust_av->request_type);
+	wpabuf_put_u8(buf, robust_av->up_bitmap);
+	wpabuf_put_u8(buf, robust_av->up_limit);
+	wpabuf_put_le32(buf, robust_av->stream_timeout);
+
+	if (robust_av->request_type != SCS_REQ_REMOVE) {
+		/* TCLAS mask element */
+		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+		len1 = wpabuf_put(buf, 1);
+		wpabuf_put_u8(buf, WLAN_EID_EXT_TCLAS_MASK);
+
+		/* Frame classifier */
+		wpabuf_put_data(buf, robust_av->frame_classifier,
+				robust_av->frame_classifier_len);
+		*len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
+	}
+
+	*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+}
+
+
+int wpas_send_mscs_req(struct wpa_supplicant *wpa_s)
+{
+	struct wpabuf *buf;
+	const u8 *ext_capab = NULL;
+	size_t buf_len;
+	int ret;
+
+	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
+		return 0;
+
+	if (wpa_s->current_bss)
+		ext_capab = wpa_bss_get_ie(wpa_s->current_bss,
+					   WLAN_EID_EXT_CAPAB);
+
+	if (!ext_capab || ext_capab[1] < 11 || !(ext_capab[12] & 0x20)) {
+		wpa_dbg(wpa_s, MSG_INFO,
+			"AP does not support MSCS - could not send MSCS Req");
+		return -1;
+	}
+
+	if (!wpa_s->mscs_setup_done &&
+	    wpa_s->robust_av.request_type != SCS_REQ_ADD) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"MSCS: Failed to send MSCS Request: request type invalid");
+		return -1;
+	}
+
+	buf_len = 3 +	/* Action frame header */
+		  3 +	/* MSCS descriptor IE header */
+		  1 +	/* Request type */
+		  2 +	/* User priority control */
+		  4 +	/* Stream timeout */
+		  3 +	/* TCLAS Mask IE header */
+		  wpa_s->robust_av.frame_classifier_len;
+
+	buf = wpabuf_alloc(buf_len);
+	if (!buf) {
+		wpa_printf(MSG_ERROR, "Failed to allocate MSCS req");
+		return -1;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
+	wpabuf_put_u8(buf, ROBUST_AV_MSCS_REQ);
+	wpa_s->robust_av.dialog_token++;
+	wpabuf_put_u8(buf, wpa_s->robust_av.dialog_token);
+
+	/* MSCS descriptor element */
+	wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, buf);
+
+	wpa_hexdump_buf(MSG_MSGDUMP, "MSCS Request", buf);
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (ret < 0)
+		wpa_dbg(wpa_s, MSG_INFO, "MSCS: Failed to send MSCS Request");
+
+	wpabuf_free(buf);
+	return ret;
+}
+
+
+void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
+				       const u8 *src, const u8 *buf, size_t len)
+{
+	u8 dialog_token;
+	u16 status_code;
+
+	if (len < 3)
+		return;
+
+	dialog_token = *buf++;
+	if (dialog_token != wpa_s->robust_av.dialog_token) {
+		wpa_printf(MSG_INFO,
+			   "MSCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
+			   dialog_token, wpa_s->robust_av.dialog_token);
+		return;
+	}
+
+	status_code = *buf;
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
+		" status_code=%u", MAC2STR(src), status_code);
+	wpa_s->mscs_setup_done = status_code == WLAN_STATUS_SUCCESS;
+}
+
+
+void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
+				 const u8 *ies, size_t ies_len)
+{
+	const u8 *mscs_desc_ie, *mscs_status;
+	u16 status;
+
+	/* Process optional MSCS Status subelement when MSCS IE is in
+	 * (Re)Association Response frame */
+	if (!ies || ies_len == 0 || !wpa_s->robust_av.valid_config)
+		return;
+
+	mscs_desc_ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_MSCS_DESCRIPTOR);
+	if (!mscs_desc_ie || mscs_desc_ie[1] <= 8)
+		return;
+
+	/* Subelements start after (ie_id(1) + ie_len(1) + ext_id(1) +
+	 * request type(1) + upc(2) + stream timeout(4) =) 10.
+	 */
+	mscs_status = get_ie(&mscs_desc_ie[10], mscs_desc_ie[1] - 8,
+			     MCSC_SUBELEM_STATUS);
+	if (!mscs_status || mscs_status[1] < 2)
+		return;
+
+	status = WPA_GET_LE16(mscs_status + 2);
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
+		" status_code=%u", MAC2STR(bssid), status);
+	wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS;
+}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index b475730..b9e4162 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -237,6 +237,10 @@
 		if (wpa_s->disconnected)
 			retry = 0;
 
+		/* do not retry if operation is not supported */
+		if (ret == -EOPNOTSUPP)
+			retry = 0;
+
 		wpa_supplicant_notify_scanning(wpa_s, 0);
 		wpas_notify_scan_done(wpa_s, 0);
 		if (wpa_s->wpa_state == WPA_SCANNING)
@@ -2187,6 +2191,60 @@
 }
 
 
+/* Minimum SNR required to achieve a certain bitrate. */
+struct minsnr_bitrate_entry {
+	int minsnr;
+	unsigned int bitrate; /* in Mbps */
+};
+
+/* VHT needs to be enabled in order to achieve MCS8 and MCS9 rates. */
+static const int vht_mcs = 8;
+
+static const struct minsnr_bitrate_entry vht20_table[] = {
+	{ 0, 0 },
+	{ 2, 6500 },   /* HT20 MCS0 */
+	{ 5, 13000 },  /* HT20 MCS1 */
+	{ 9, 19500 },  /* HT20 MCS2 */
+	{ 11, 26000 }, /* HT20 MCS3 */
+	{ 15, 39000 }, /* HT20 MCS4 */
+	{ 18, 52000 }, /* HT20 MCS5 */
+	{ 20, 58500 }, /* HT20 MCS6 */
+	{ 25, 65000 }, /* HT20 MCS7 */
+	{ 29, 78000 }, /* VHT20 MCS8 */
+	{ -1, 78000 }  /* SNR > 29 */
+};
+
+static const struct minsnr_bitrate_entry vht40_table[] = {
+	{ 0, 0 },
+	{ 5, 13500 },   /* HT40 MCS0 */
+	{ 8, 27000 },   /* HT40 MCS1 */
+	{ 12, 40500 },  /* HT40 MCS2 */
+	{ 14, 54000 },  /* HT40 MCS3 */
+	{ 18, 81000 },  /* HT40 MCS4 */
+	{ 21, 108000 }, /* HT40 MCS5 */
+	{ 23, 121500 }, /* HT40 MCS6 */
+	{ 28, 135000 }, /* HT40 MCS7 */
+	{ 32, 162000 }, /* VHT40 MCS8 */
+	{ 34, 180000 }, /* VHT40 MCS9 */
+	{ -1, 180000 }  /* SNR > 34 */
+};
+
+static const struct minsnr_bitrate_entry vht80_table[] = {
+	{ 0, 0 },
+	{ 8, 29300 },   /* VHT80 MCS0 */
+	{ 11, 58500 },  /* VHT80 MCS1 */
+	{ 15, 87800 },  /* VHT80 MCS2 */
+	{ 17, 117000 }, /* VHT80 MCS3 */
+	{ 21, 175500 }, /* VHT80 MCS4 */
+	{ 24, 234000 }, /* VHT80 MCS5 */
+	{ 26, 263300 }, /* VHT80 MCS6 */
+	{ 31, 292500 }, /* VHT80 MCS7 */
+	{ 35, 351000 }, /* VHT80 MCS8 */
+	{ 37, 390000 }, /* VHT80 MCS9 */
+	{ -1, 390000 }  /* SNR > 37 */
+};
+
+
 static unsigned int interpolate_rate(int snr, int snr0, int snr1,
 				     int rate0, int rate1)
 {
@@ -2194,68 +2252,42 @@
 }
 
 
-#define INTERPOLATE_RATE(snr0, snr1, rate0, rate1) \
-	if (snr < (snr1)) \
-		return interpolate_rate(snr, (snr0), (snr1), (rate0), (rate1))
-
-static unsigned int max_ht20_rate(int snr, int vht)
+static unsigned int max_rate(const struct minsnr_bitrate_entry table[],
+			     int snr, bool vht)
 {
-	if (snr < 0)
-		return 0;
-	INTERPOLATE_RATE(0, 2, 0, 6500); /* HT20 MCS0 */
-	INTERPOLATE_RATE(2, 5, 6500, 13000); /* HT20 MCS1 */
-	INTERPOLATE_RATE(5, 9, 13000, 19500); /* HT20 MCS2 */
-	INTERPOLATE_RATE(9, 11, 19500, 26000); /* HT20 MCS3 */
-	INTERPOLATE_RATE(11, 15, 26000, 39000); /* HT20 MCS4 */
-	INTERPOLATE_RATE(15, 18, 39000, 52000); /* HT20 MCS5 */
-	INTERPOLATE_RATE(18, 20, 52000, 58500); /* HT20 MCS6 */
-	INTERPOLATE_RATE(20, 25, 58500, 65000); /* HT20 MCS7 */
-	if (!vht)
-		return 65000;
-	INTERPOLATE_RATE(25, 29, 65000, 78000); /* VHT20 MCS8 */
-	return 78000;
+	const struct minsnr_bitrate_entry *prev, *entry = table;
+
+	while ((entry->minsnr != -1) &&
+	       (snr >= entry->minsnr) &&
+	       (vht || entry - table <= vht_mcs))
+		entry++;
+	if (entry == table)
+		return entry->bitrate;
+	prev = entry - 1;
+	if (entry->minsnr == -1 || (!vht && entry - table > vht_mcs))
+		return prev->bitrate;
+	return interpolate_rate(snr, prev->minsnr, entry->minsnr, prev->bitrate,
+				entry->bitrate);
 }
 
 
-static unsigned int max_ht40_rate(int snr, int vht)
+static unsigned int max_ht20_rate(int snr, bool vht)
 {
-	if (snr < 0)
-		return 0;
-	INTERPOLATE_RATE(0, 5, 0, 13500); /* HT40 MCS0 */
-	INTERPOLATE_RATE(5, 8, 13500, 27000); /* HT40 MCS1 */
-	INTERPOLATE_RATE(8, 12, 27000, 40500); /* HT40 MCS2 */
-	INTERPOLATE_RATE(12, 14, 40500, 54000); /* HT40 MCS3 */
-	INTERPOLATE_RATE(14, 18, 54000, 81000); /* HT40 MCS4 */
-	INTERPOLATE_RATE(18, 21, 81000, 108000); /* HT40 MCS5 */
-	INTERPOLATE_RATE(21, 23, 108000, 121500); /* HT40 MCS6 */
-	INTERPOLATE_RATE(23, 28, 121500, 135000); /* HT40 MCS7 */
-	if (!vht)
-		return 135000;
-	INTERPOLATE_RATE(28, 32, 135000, 162000); /* VHT40 MCS8 */
-	INTERPOLATE_RATE(32, 34, 162000, 180000); /* VHT40 MCS9 */
-	return 180000;
+	return max_rate(vht20_table, snr, vht);
+}
+
+
+static unsigned int max_ht40_rate(int snr, bool vht)
+{
+	return max_rate(vht40_table, snr, vht);
 }
 
 
 static unsigned int max_vht80_rate(int snr)
 {
-	if (snr < 0)
-		return 0;
-	INTERPOLATE_RATE(0, 8, 0, 29300); /* VHT80 MCS0 */
-	INTERPOLATE_RATE(8, 11, 29300, 58500); /* VHT80 MCS1 */
-	INTERPOLATE_RATE(11, 15, 58500, 87800); /* VHT80 MCS2 */
-	INTERPOLATE_RATE(15, 17, 87800, 117000); /* VHT80 MCS3 */
-	INTERPOLATE_RATE(17, 21, 117000, 175500); /* VHT80 MCS4 */
-	INTERPOLATE_RATE(21, 24, 175500, 234000); /* VHT80 MCS5 */
-	INTERPOLATE_RATE(24, 26, 234000, 263300); /* VHT80 MCS6 */
-	INTERPOLATE_RATE(26, 31, 263300, 292500); /* VHT80 MCS7 */
-	INTERPOLATE_RATE(31, 35, 292500, 351000); /* VHT80 MCS8 */
-	INTERPOLATE_RATE(35, 37, 351000, 390000); /* VHT80 MCS9 */
-	return 390000;
+	return max_rate(vht80_table, snr, 1);
 }
 
-#undef INTERPOLATE_RATE
-
 
 unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
 			      const u8 *ies, size_t ies_len, int rate,
@@ -2309,7 +2341,7 @@
 	if (capab == CAPAB_HT || capab == CAPAB_HT40 || capab == CAPAB_VHT) {
 		ie = get_ie(ies, ies_len, WLAN_EID_HT_CAP);
 		if (ie) {
-			tmp = max_ht20_rate(snr, 0);
+			tmp = max_ht20_rate(snr, false);
 			if (tmp > est)
 				est = tmp;
 		}
@@ -2319,7 +2351,7 @@
 		ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
 		if (ie && ie[1] >= 2 &&
 		    (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
-			tmp = max_ht40_rate(snr, 0);
+			tmp = max_ht40_rate(snr, false);
 			if (tmp > est)
 				est = tmp;
 		}
@@ -2329,7 +2361,7 @@
 		/* Use +1 to assume VHT is always faster than HT */
 		ie = get_ie(ies, ies_len, WLAN_EID_VHT_CAP);
 		if (ie) {
-			tmp = max_ht20_rate(snr, 1) + 1;
+			tmp = max_ht20_rate(snr, true) + 1;
 			if (tmp > est)
 				est = tmp;
 
@@ -2337,7 +2369,7 @@
 			if (ie && ie[1] >= 2 &&
 			    (ie[3] &
 			     HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
-				tmp = max_ht40_rate(snr, 1) + 1;
+				tmp = max_ht40_rate(snr, true) + 1;
 				if (tmp > est)
 					est = tmp;
 			}
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index d06f6e2..7fec1cd 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -85,16 +85,21 @@
 static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
 						 struct wpa_ssid *ssid,
 						 const u8 *bssid, int external,
-						 int reuse, int *ret_use_pt)
+						 int reuse, int *ret_use_pt,
+						 bool *ret_use_pk)
 {
 	struct wpabuf *buf;
 	size_t len;
 	const char *password;
 	struct wpa_bss *bss;
 	int use_pt = 0;
+	bool use_pk = false;
+	u8 rsnxe_capa = 0;
 
 	if (ret_use_pt)
 		*ret_use_pt = 0;
+	if (ret_use_pk)
+		*ret_use_pk = false;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (wpa_s->sae_commit_override) {
@@ -123,7 +128,8 @@
 	    os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
 		wpa_printf(MSG_DEBUG,
 			   "SAE: Reuse previously generated PWE on a retry with the same AP");
-		use_pt = wpa_s->sme.sae.tmp->h2e;
+		use_pt = wpa_s->sme.sae.h2e;
+		use_pk = wpa_s->sme.sae.pk;
 		goto reuse_data;
 	}
 	if (sme_set_sae_group(wpa_s) < 0) {
@@ -131,19 +137,37 @@
 		return NULL;
 	}
 
+	bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+	if (bss) {
+		const u8 *rsnxe;
+
+		rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+		if (rsnxe && rsnxe[1] >= 1)
+			rsnxe_capa = rsnxe[2];
+	}
+
 	if (ssid->sae_password_id && wpa_s->conf->sae_pwe != 3)
 		use_pt = 1;
+#ifdef CONFIG_SAE_PK
+	if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
+	    ssid->sae_pk != SAE_PK_MODE_DISABLED &&
+	    ((ssid->sae_password &&
+	      sae_pk_valid_password(ssid->sae_password)) ||
+	     (!ssid->sae_password && ssid->passphrase &&
+	      sae_pk_valid_password(ssid->passphrase)))) {
+		use_pt = 1;
+		use_pk = true;
+	}
+
+	if (ssid->sae_pk == SAE_PK_MODE_ONLY && !use_pk) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Cannot use PK with the selected AP");
+		return NULL;
+	}
+#endif /* CONFIG_SAE_PK */
 
 	if (use_pt || wpa_s->conf->sae_pwe == 1 || wpa_s->conf->sae_pwe == 2) {
-		bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
-		if (bss) {
-			const u8 *rsnxe;
-
-			rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
-			if (rsnxe && rsnxe[1] >= 1)
-				use_pt = !!(rsnxe[2] &
-					    BIT(WLAN_RSNX_CAPAB_SAE_H2E));
-		}
+		use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E));
 
 		if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
 		    wpa_s->conf->sae_pwe != 3 &&
@@ -157,7 +181,7 @@
 	if (use_pt &&
 	    sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt,
 				  wpa_s->own_addr, bssid,
-				  wpa_s->sme.sae_rejected_groups) < 0)
+				  wpa_s->sme.sae_rejected_groups, NULL) < 0)
 		return NULL;
 	if (!use_pt &&
 	    sae_prepare_commit(wpa_s->own_addr, bssid,
@@ -167,8 +191,17 @@
 		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
 		return NULL;
 	}
-	if (wpa_s->sme.sae.tmp)
+	if (wpa_s->sme.sae.tmp) {
 		os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN);
+		if (use_pt && use_pk)
+			wpa_s->sme.sae.pk = 1;
+#ifdef CONFIG_SAE_PK
+		os_memcpy(wpa_s->sme.sae.tmp->own_addr, wpa_s->own_addr,
+			  ETH_ALEN);
+		os_memcpy(wpa_s->sme.sae.tmp->peer_addr, bssid, ETH_ALEN);
+		sae_pk_set_password(&wpa_s->sme.sae, password);
+#endif /* CONFIG_SAE_PK */
+	}
 
 reuse_data:
 	len = wpa_s->sme.sae_token ? 3 + wpabuf_len(wpa_s->sme.sae_token) : 0;
@@ -179,8 +212,12 @@
 		return NULL;
 	if (!external) {
 		wpabuf_put_le16(buf, 1); /* Transaction seq# */
-		wpabuf_put_le16(buf, use_pt ? WLAN_STATUS_SAE_HASH_TO_ELEMENT :
-				WLAN_STATUS_SUCCESS);
+		if (use_pk)
+			wpabuf_put_le16(buf, WLAN_STATUS_SAE_PK);
+		else if (use_pt)
+			wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+		else
+			wpabuf_put_le16(buf,WLAN_STATUS_SUCCESS);
 	}
 	if (sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
 			     ssid->sae_password_id) < 0) {
@@ -189,6 +226,8 @@
 	}
 	if (ret_use_pt)
 		*ret_use_pt = use_pt;
+	if (ret_use_pk)
+		*ret_use_pk = use_pk;
 
 	return buf;
 }
@@ -716,7 +755,8 @@
 		if (start)
 			resp = sme_auth_build_sae_commit(wpa_s, ssid,
 							 bss->bssid, 0,
-							 start == 2, NULL);
+							 start == 2, NULL,
+							 NULL);
 		else
 			resp = sme_auth_build_sae_confirm(wpa_s, 0);
 		if (resp == NULL) {
@@ -1008,8 +1048,11 @@
 {
 	struct wpabuf *resp, *buf;
 	int use_pt;
+	bool use_pk;
+	u16 status;
 
-	resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt);
+	resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt,
+					 &use_pk);
 	if (!resp) {
 		wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
 		return -1;
@@ -1023,10 +1066,14 @@
 	}
 
 	wpa_s->sme.seq_num++;
+	if (use_pk)
+		status = WLAN_STATUS_SAE_PK;
+	else if (use_pt)
+		status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
+	else
+		status = WLAN_STATUS_SUCCESS;
 	sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
-				    bssid, 1, wpa_s->sme.seq_num,
-				    use_pt ? WLAN_STATUS_SAE_HASH_TO_ELEMENT :
-				    WLAN_STATUS_SUCCESS);
+				    bssid, 1, wpa_s->sme.seq_num, status);
 	wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0);
 	wpabuf_free(resp);
 	wpabuf_free(buf);
@@ -1223,8 +1270,7 @@
 		wpabuf_free(wpa_s->sme.sae_token);
 		token_pos = data + sizeof(le16);
 		token_len = len - sizeof(le16);
-		if (wpa_s->sme.sae.tmp)
-			h2e = wpa_s->sme.sae.tmp->h2e;
+		h2e = wpa_s->sme.sae.h2e;
 		if (h2e) {
 			if (token_len < 3) {
 				wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1287,7 +1333,8 @@
 	}
 
 	if (status_code != WLAN_STATUS_SUCCESS &&
-	    status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+	    status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT &&
+	    status_code != WLAN_STATUS_SAE_PK)
 		return -1;
 
 	if (auth_transaction == 1) {
@@ -1304,24 +1351,30 @@
 				   "SAE: Ignore commit message while waiting for confirm");
 			return 0;
 		}
-		if (wpa_s->sme.sae.tmp && wpa_s->sme.sae.tmp->h2e &&
-		    status_code == WLAN_STATUS_SUCCESS) {
+		if (wpa_s->sme.sae.h2e && status_code == WLAN_STATUS_SUCCESS) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Unexpected use of status code 0 in SAE commit when H2E was expected");
 			return -1;
 		}
-		if (wpa_s->sme.sae.tmp && !wpa_s->sme.sae.tmp->h2e &&
+		if ((!wpa_s->sme.sae.h2e || wpa_s->sme.sae.pk) &&
 		    status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Unexpected use of status code for H2E in SAE commit when H2E was not expected");
 			return -1;
 		}
+		if (!wpa_s->sme.sae.pk &&
+		    status_code == WLAN_STATUS_SAE_PK) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Unexpected use of status code for PK in SAE commit when PK was not expected");
+			return -1;
+		}
 
 		if (groups && groups[0] <= 0)
 			groups = NULL;
 		res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
 				       groups, status_code ==
-				       WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+				       WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+				       status_code == WLAN_STATUS_SAE_PK);
 		if (res == SAE_SILENTLY_DISCARD) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Drop commit message due to reflection attack");
@@ -1827,6 +1880,42 @@
 pfs_fail:
 #endif /* CONFIG_DPP2 */
 
+	wpa_s->mscs_setup_done = false;
+	if (wpa_s->current_bss && wpa_s->robust_av.valid_config) {
+		struct wpabuf *mscs_ie;
+		size_t mscs_ie_len, buf_len, *wpa_ie_len, max_ie_len;
+
+		if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_MSCS))
+			goto mscs_fail;
+
+		buf_len = 3 +	/* MSCS descriptor IE header */
+			  1 +	/* Request type */
+			  2 +	/* User priority control */
+			  4 +	/* Stream timeout */
+			  3 +	/* TCLAS Mask IE header */
+			  wpa_s->robust_av.frame_classifier_len;
+		mscs_ie = wpabuf_alloc(buf_len);
+		if (!mscs_ie) {
+			wpa_printf(MSG_INFO,
+				   "MSCS: Failed to allocate MSCS IE");
+			goto mscs_fail;
+		}
+
+		wpa_ie_len = &wpa_s->sme.assoc_req_ie_len;
+		max_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
+		wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, mscs_ie);
+		if ((*wpa_ie_len + wpabuf_len(mscs_ie)) <= max_ie_len) {
+			wpa_hexdump_buf(MSG_MSGDUMP, "MSCS IE", mscs_ie);
+			mscs_ie_len = wpabuf_len(mscs_ie);
+			os_memcpy(wpa_s->sme.assoc_req_ie + *wpa_ie_len,
+				  wpabuf_head(mscs_ie), mscs_ie_len);
+			*wpa_ie_len += mscs_ie_len;
+		}
+
+		wpabuf_free(mscs_ie);
+	}
+mscs_fail:
+
 	if (ssid && ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
 
@@ -2573,6 +2662,16 @@
 			return;
 		}
 
+#ifdef CONFIG_TESTING_OPTIONS
+		if (wpa_s->oci_freq_override_saquery_req) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override SA Query Request OCI frequency %d -> %d MHz",
+				   ci.frequency,
+				   wpa_s->oci_freq_override_saquery_req);
+			ci.frequency = wpa_s->oci_freq_override_saquery_req;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 		if (ocv_insert_extended_oci(&ci, req + req_len) < 0)
 			return;
 
@@ -2727,6 +2826,16 @@
 			return;
 		}
 
+#ifdef CONFIG_TESTING_OPTIONS
+		if (wpa_s->oci_freq_override_saquery_resp) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override SA Query Response OCI frequency %d -> %d MHz",
+				   ci.frequency,
+				   wpa_s->oci_freq_override_saquery_resp);
+			ci.frequency = wpa_s->oci_freq_override_saquery_resp;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 		if (ocv_insert_extended_oci(&ci, resp + resp_len) < 0)
 			return;
 
@@ -2806,8 +2915,11 @@
 
 		if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
 					 channel_width_to_int(ci.chanwidth),
-					 ci.seg1_idx) != 0) {
-			wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+					 ci.seg1_idx) != OCI_SUCCESS) {
+			wpa_msg(wpa_s, MSG_INFO, OCV_FAILURE "addr=" MACSTR
+				" frame=saquery%s error=%s",
+				MAC2STR(sa), data[0] == WLAN_SA_QUERY_REQUEST ?
+				"req" : "resp", ocv_errorstr);
 			return;
 		}
 	}
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 2e09102..c706a4e 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -121,6 +121,15 @@
 			os_free(wnmtfs_ie);
 			return -1;
 		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (wpa_s->oci_freq_override_wnm_sleep) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI KDE frequency %d -> %d MHz",
+				   ci.frequency,
+				   wpa_s->oci_freq_override_wnm_sleep);
+			ci.frequency = wpa_s->oci_freq_override_wnm_sleep;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 		oci_ie_len = OCV_OCI_EXTENDED_LEN;
 		oci_ie = os_zalloc(oci_ie_len);
@@ -374,8 +383,9 @@
 
 		if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
 					 channel_width_to_int(ci.chanwidth),
-					 ci.seg1_idx) != 0) {
-			wpa_msg(wpa_s, MSG_WARNING, "WNM: %s", ocv_errorstr);
+					 ci.seg1_idx) != OCI_SUCCESS) {
+			wpa_msg(wpa_s, MSG_WARNING, "WNM: OCV failed: %s",
+				ocv_errorstr);
 			return;
 		}
 	}
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 6a2d2c3..51c9642 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -2994,6 +2994,13 @@
 }
 
 
+static int wpa_cli_cmd_dpp_bootstrap_set(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_SET", 1, argc, argv);
+}
+
+
 static int wpa_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
 				     char *argv[])
 {
@@ -3056,9 +3063,98 @@
 	return wpa_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
 }
 
+
+#ifdef CONFIG_DPP2
+
+static int wpa_cli_cmd_dpp_chirp(struct wpa_ctrl *ctrl, int argc,
+				 char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "DPP_CHIRP", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "DPP_STOP_CHIRP");
+}
+
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
 
 
+static int wpa_ctrl_command_bss(struct wpa_ctrl *ctrl, const char *cmd)
+{
+	char buf[512], *pos, *bssid, *freq, *level, *flags, *ssid;
+	size_t len;
+	int ret, id = -1;
+
+	if (!ctrl_conn)
+		return -1;
+	len = sizeof(buf) - 1;
+	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
+			       wpa_cli_msg_cb);
+	if (ret == -2) {
+		printf("'%s' command timed out.\n", cmd);
+		return -2;
+	} else if (ret < 0) {
+		printf("'%s' command failed.\n", cmd);
+		return -1;
+	}
+
+	buf[len] = '\0';
+	if (os_memcmp(buf, "FAIL", 4) == 0)
+		return -1;
+
+	pos = buf;
+	while (*pos != '\0') {
+		if (str_starts(pos, "id="))
+			id = atoi(pos + 3);
+		if (str_starts(pos, "bssid="))
+			bssid = pos + 6;
+		if (str_starts(pos, "freq="))
+			freq = pos + 5;
+		if (str_starts(pos, "level="))
+			level = pos + 6;
+		if (str_starts(pos, "flags="))
+			flags = pos + 6;
+		if (str_starts(pos, "ssid="))
+			ssid = pos + 5;
+
+		while (*pos != '\0' && *pos != '\n')
+			pos++;
+		*pos++ = '\0';
+	}
+	if (id != -1)
+		printf("%s\t%s\t%s\t%s\t%s\n", bssid, freq, level, flags, ssid);
+	return id;
+}
+
+
+static int wpa_cli_cmd_all_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[64];
+	int id = -1;
+	unsigned int mask;
+
+	printf("bssid / frequency / signal level / flags / ssid\n");
+
+	mask = WPA_BSS_MASK_ID | WPA_BSS_MASK_BSSID | WPA_BSS_MASK_FREQ |
+		WPA_BSS_MASK_LEVEL | WPA_BSS_MASK_FLAGS | WPA_BSS_MASK_SSID;
+	do {
+		if (id < 0)
+			os_snprintf(cmd, sizeof(cmd), "BSS FIRST MASK=0x%x",
+				    mask);
+		else
+			os_snprintf(cmd, sizeof(cmd), "BSS NEXT-%d MASK=0x%x",
+				    id, mask);
+		id = wpa_ctrl_command_bss(ctrl, cmd);
+	} while (id >= 0);
+
+	return 0;
+}
+
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -3692,6 +3788,9 @@
 	{ "dpp_bootstrap_info", wpa_cli_cmd_dpp_bootstrap_info, NULL,
 	  cli_cmd_flag_none,
 	  "<id> = show DPP bootstrap information" },
+	{ "dpp_bootstrap_set", wpa_cli_cmd_dpp_bootstrap_set, NULL,
+	  cli_cmd_flag_none,
+	  "<id> [conf=..] [ssid=<SSID>] [ssid_charset=#] [psk=<PSK>] [pass=<passphrase>] [configurator=<id>] [conn_status=#] [akm_use_selector=<0|1>] [group_id=..] [expiry=#] [csrattrs=..] = set DPP configurator parameters" },
 	{ "dpp_auth_init", wpa_cli_cmd_dpp_auth_init, NULL, cli_cmd_flag_none,
 	  "peer=<id> [own=<id>] = initiate DPP bootstrapping" },
 	{ "dpp_listen", wpa_cli_cmd_dpp_listen, NULL, cli_cmd_flag_none,
@@ -3717,7 +3816,17 @@
 	{ "dpp_pkex_remove", wpa_cli_cmd_dpp_pkex_remove, NULL,
 	  cli_cmd_flag_none,
 	  "*|<id> = remove DPP pkex information" },
+#ifdef CONFIG_DPP2
+	{ "dpp_chirp", wpa_cli_cmd_dpp_chirp, NULL,
+	  cli_cmd_flag_none,
+	  "own=<BI ID> iter=<count> = start DPP chirp" },
+	{ "dpp_stop_chirp", wpa_cli_cmd_dpp_stop_chirp, NULL,
+	  cli_cmd_flag_none,
+	  "= stop DPP chirp" },
+#endif /* CONFIG_DPP2 */
 #endif /* CONFIG_DPP */
+	{ "all_bss", wpa_cli_cmd_all_bss, NULL, cli_cmd_flag_none,
+	  "= list all BSS entries (scan results)" },
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/.gitignore b/wpa_supplicant/wpa_gui-qt4/icons/.gitignore
new file mode 100644
index 0000000..8d772cc
--- /dev/null
+++ b/wpa_supplicant/wpa_gui-qt4/icons/.gitignore
@@ -0,0 +1,2 @@
+hicolor
+pixmaps
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index a01a4e5..67fb426 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -804,7 +804,22 @@
 
 #ifdef CONFIG_BGSCAN
 
-static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
+static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->bgscan_ssid) {
+		bgscan_deinit(wpa_s);
+		wpa_s->bgscan_ssid = NULL;
+	}
+}
+
+
+/**
+ * wpa_supplicant_reset_bgscan - Reset the bgscan for the current SSID.
+ * @wpa_s: Pointer to the wpa_supplicant data
+ *
+ * Stop, start, or reconfigure the scan parameters depending on the method.
+ */
+void wpa_supplicant_reset_bgscan(struct wpa_supplicant *wpa_s)
 {
 	const char *name;
 
@@ -812,12 +827,12 @@
 		name = wpa_s->current_ssid->bgscan;
 	else
 		name = wpa_s->conf->bgscan;
-	if (name == NULL || name[0] == '\0')
+	if (!name || name[0] == '\0') {
+		wpa_supplicant_stop_bgscan(wpa_s);
 		return;
+	}
 	if (wpas_driver_bss_selection(wpa_s))
 		return;
-	if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
-		return;
 #ifdef CONFIG_P2P
 	if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
 		return;
@@ -847,15 +862,6 @@
 		wpa_s->bgscan_ssid = NULL;
 }
 
-
-static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s)
-{
-	if (wpa_s->bgscan_ssid != NULL) {
-		bgscan_deinit(wpa_s);
-		wpa_s->bgscan_ssid = NULL;
-	}
-}
-
 #endif /* CONFIG_BGSCAN */
 
 
@@ -979,8 +985,7 @@
 			fils_hlp_sent ? " FILS_HLP_SENT" : "");
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 		wpas_clear_temp_disabled(wpa_s, ssid, 1);
-		wpa_blacklist_clear(wpa_s);
-		wpa_s->extra_blacklist_count = 0;
+		wpa_s->consecutive_conn_failures = 0;
 		wpa_s->new_connection = 0;
 		wpa_drv_set_operstate(wpa_s, 1);
 #ifndef IEEE8021X_EAPOL
@@ -1012,8 +1017,8 @@
 	wpa_s->wpa_state = state;
 
 #ifdef CONFIG_BGSCAN
-	if (state == WPA_COMPLETED)
-		wpa_supplicant_start_bgscan(wpa_s);
+	if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
+		wpa_supplicant_reset_bgscan(wpa_s);
 	else if (state < WPA_ASSOCIATED)
 		wpa_supplicant_stop_bgscan(wpa_s);
 #endif /* CONFIG_BGSCAN */
@@ -1185,6 +1190,7 @@
 		wpa_s->reassociate = 1;
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	}
+	wpa_blacklist_clear(wpa_s);
 	wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
 	return 0;
 }
@@ -1640,9 +1646,26 @@
 	if (ssid->sae_password_id && sae_pwe != 3)
 		sae_pwe = 1;
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, sae_pwe);
+#ifdef CONFIG_SAE_PK
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PK,
+			 wpa_key_mgmt_sae(ssid->key_mgmt) &&
+			 ssid->sae_pk != SAE_PK_MODE_DISABLED &&
+			 ((ssid->sae_password &&
+			   sae_pk_valid_password(ssid->sae_password)) ||
+			  (!ssid->sae_password && ssid->passphrase &&
+			   sae_pk_valid_password(ssid->passphrase))));
+#endif /* CONFIG_SAE_PK */
 #ifdef CONFIG_TESTING_OPTIONS
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_RSNXE_USED,
 			 wpa_s->ft_rsnxe_used);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL,
+			 wpa_s->oci_freq_override_eapol);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL_G2,
+			 wpa_s->oci_freq_override_eapol_g2);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FT_ASSOC,
+			 wpa_s->oci_freq_override_ft_assoc);
+	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FILS_ASSOC,
+			 wpa_s->oci_freq_override_fils_assoc);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	/* Extended Key ID is only supported in infrastructure BSS so far */
@@ -1883,6 +1906,9 @@
 			*pos |= 0x01;
 #endif /* CONFIG_FILS */
 		break;
+	case 10: /* Bits 80-87 */
+		*pos |= 0x20; /* Bit 85 - Mirrored SCS */
+		break;
 	}
 }
 
@@ -1890,7 +1916,7 @@
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
 {
 	u8 *pos = buf;
-	u8 len = 10, i;
+	u8 len = 11, i;
 
 	if (len < wpa_s->extended_capa_len)
 		len = wpa_s->extended_capa_len;
@@ -2060,7 +2086,9 @@
 	if (!password)
 		password = ssid->passphrase;
 
-	if ((conf->sae_pwe == 0 && !ssid->sae_password_id) || !password ||
+	if (!password ||
+	    (conf->sae_pwe == 0 && !ssid->sae_password_id &&
+	     !sae_pk_valid_password(password)) ||
 	    conf->sae_pwe == 3) {
 		/* PT derivation not needed */
 		sae_deinit_pt(ssid->pt);
@@ -3171,6 +3199,39 @@
 		wpa_ie_len += wpa_s->rsnxe_len;
 	}
 
+	if (bss && wpa_s->robust_av.valid_config) {
+		struct wpabuf *mscs_ie;
+		size_t mscs_ie_len, buf_len;
+
+		if (!wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_MSCS))
+			goto mscs_fail;
+
+		buf_len = 3 +	/* MSCS descriptor IE header */
+			  1 +	/* Request type */
+			  2 +	/* User priority control */
+			  4 +	/* Stream timeout */
+			  3 +	/* TCLAS Mask IE header */
+			  wpa_s->robust_av.frame_classifier_len;
+		mscs_ie = wpabuf_alloc(buf_len);
+		if (!mscs_ie) {
+			wpa_printf(MSG_INFO,
+				   "MSCS: Failed to allocate MSCS IE");
+			goto mscs_fail;
+		}
+
+		wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, mscs_ie);
+		if ((wpa_ie_len + wpabuf_len(mscs_ie)) <= max_wpa_ie_len) {
+			wpa_hexdump_buf(MSG_MSGDUMP, "MSCS IE", mscs_ie);
+			mscs_ie_len = wpabuf_len(mscs_ie);
+			os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(mscs_ie),
+				  mscs_ie_len);
+			wpa_ie_len += mscs_ie_len;
+		}
+
+		wpabuf_free(mscs_ie);
+	}
+mscs_fail:
+
 	if (ssid->multi_ap_backhaul_sta) {
 		size_t multi_ap_ie_len;
 
@@ -3485,6 +3546,7 @@
 	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 	wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
 	wpa_s->rsnxe_len = 0;
+	wpa_s->mscs_setup_done = false;
 
 	wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, &params, NULL);
 	if (!wpa_ie) {
@@ -4062,6 +4124,52 @@
 
 
 /**
+ * wpa_supplicant_remove_all_networks - Remove all configured networks
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: 0 on success (errors are currently ignored)
+ *
+ * This function performs the following operations:
+ * 1. Remove all networks.
+ * 2. Send network removal notifications.
+ * 3. Update internal state machines.
+ * 4. Stop any running sched scans.
+ */
+int wpa_supplicant_remove_all_networks(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid;
+
+	if (wpa_s->sched_scanning)
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+
+	eapol_sm_invalidate_cached_session(wpa_s->eapol);
+	if (wpa_s->current_ssid) {
+#ifdef CONFIG_SME
+		wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+		wpa_sm_set_config(wpa_s->wpa, NULL);
+		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+		if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+			wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(
+			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+	}
+	ssid = wpa_s->conf->ssid;
+	while (ssid) {
+		struct wpa_ssid *remove_ssid = ssid;
+		int id;
+
+		id = ssid->id;
+		ssid = ssid->next;
+		if (wpa_s->last_ssid == remove_ssid)
+			wpa_s->last_ssid = NULL;
+		wpas_notify_network_removed(wpa_s, remove_ssid);
+		wpa_config_remove_network(wpa_s->conf, id);
+	}
+	return 0;
+}
+
+
+/**
  * wpa_supplicant_enable_network - Mark a configured network as enabled
  * @wpa_s: wpa_supplicant structure for a network interface
  * @ssid: wpa_ssid structure for a configured network or %NULL
@@ -4851,6 +4959,65 @@
 }
 
 
+int wpa_supplicant_update_bridge_ifname(struct wpa_supplicant *wpa_s,
+					const char *bridge_ifname)
+{
+	if (wpa_s->wpa_state > WPA_SCANNING)
+		return -EBUSY;
+
+	if (bridge_ifname &&
+	    os_strlen(bridge_ifname) >= sizeof(wpa_s->bridge_ifname))
+		return -EINVAL;
+
+	if (!bridge_ifname)
+		bridge_ifname = "";
+
+	if (os_strcmp(wpa_s->bridge_ifname, bridge_ifname) == 0)
+		return 0;
+
+	if (wpa_s->l2_br) {
+		l2_packet_deinit(wpa_s->l2_br);
+		wpa_s->l2_br = NULL;
+	}
+
+	os_strlcpy(wpa_s->bridge_ifname, bridge_ifname,
+		   sizeof(wpa_s->bridge_ifname));
+
+	if (wpa_s->bridge_ifname[0]) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Receiving packets from bridge interface '%s'",
+			wpa_s->bridge_ifname);
+		wpa_s->l2_br = l2_packet_init_bridge(
+			wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr,
+			ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1);
+		if (!wpa_s->l2_br) {
+			wpa_msg(wpa_s, MSG_ERROR,
+				"Failed to open l2_packet connection for the bridge interface '%s'",
+				wpa_s->bridge_ifname);
+			goto fail;
+		}
+	}
+
+#ifdef CONFIG_TDLS
+	if (!wpa_s->p2p_mgmt && wpa_tdls_init(wpa_s->wpa))
+		goto fail;
+#endif /* CONFIG_TDLS */
+
+	return 0;
+fail:
+	wpa_s->bridge_ifname[0] = 0;
+	if (wpa_s->l2_br) {
+		l2_packet_deinit(wpa_s->l2_br);
+		wpa_s->l2_br = NULL;
+	}
+#ifdef CONFIG_TDLS
+	if (!wpa_s->p2p_mgmt)
+		wpa_tdls_init(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+	return -EIO;
+}
+
+
 /**
  * wpa_supplicant_driver_init - Initialize driver interface parameters
  * @wpa_s: Pointer to wpa_supplicant data
@@ -6034,6 +6201,8 @@
 	wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
 	if (wpa_s->drv_priv == NULL) {
 		const char *pos;
+		int level = MSG_ERROR;
+
 		pos = driver ? os_strchr(driver, ',') : NULL;
 		if (pos) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
@@ -6041,8 +6210,12 @@
 			driver = pos + 1;
 			goto next_driver;
 		}
-		wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
-			"interface");
+
+#ifdef CONFIG_MATCH_IFACE
+		if (wpa_s->matched == WPA_IFACE_MATCHED_NULL)
+			level = MSG_DEBUG;
+#endif /* CONFIG_MATCH_IFACE */
+		wpa_msg(wpa_s, level, "Failed to initialize driver interface");
 		return -1;
 	}
 	if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
@@ -6187,6 +6360,9 @@
 		return -1;
 	}
 	os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname));
+#ifdef CONFIG_MATCH_IFACE
+	wpa_s->matched = iface->matched;
+#endif /* CONFIG_MATCH_IFACE */
 
 	if (iface->bridge_ifname) {
 		if (os_strlen(iface->bridge_ifname) >=
@@ -6578,6 +6754,10 @@
 			if (!iface)
 				return NULL;
 			*iface = *miface;
+			if (!miface->ifname)
+				iface->matched = WPA_IFACE_MATCHED_NULL;
+			else
+				iface->matched = WPA_IFACE_MATCHED;
 			iface->ifname = ifname;
 			return iface;
 		}
@@ -6612,8 +6792,6 @@
 		if (iface) {
 			wpa_s = wpa_supplicant_add_iface(global, iface, NULL);
 			os_free(iface);
-			if (wpa_s)
-				wpa_s->matched = 1;
 		}
 	}
 
@@ -7143,6 +7321,18 @@
 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_DISABLE_BTM)
 		wpa_supplicant_set_default_scan_ies(wpa_s);
 
+#ifdef CONFIG_BGSCAN
+	/*
+	 * We default to global bgscan parameters only when per-network bgscan
+	 * parameters aren't set. Only bother resetting bgscan parameters if
+	 * this is the case.
+	 */
+	if ((wpa_s->conf->changed_parameters & CFG_CHANGED_BGSCAN) &&
+	    wpa_s->current_ssid && !wpa_s->current_ssid->bgscan &&
+	    wpa_s->wpa_state == WPA_COMPLETED)
+		wpa_supplicant_reset_bgscan(wpa_s);
+#endif /* CONFIG_BGSCAN */
+
 #ifdef CONFIG_WPS
 	wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
@@ -7183,7 +7373,7 @@
 			continue;
 		if (bss->ssid_len == cbss->ssid_len &&
 		    os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 &&
-		    wpa_blacklist_get(wpa_s, bss->bssid) == NULL) {
+		    !wpa_blacklist_is_blacklisted(wpa_s, bss->bssid)) {
 			add_freq(freqs, &num_freqs, bss->freq);
 			if (num_freqs == max_freqs)
 				break;
@@ -7237,10 +7427,6 @@
 	/*
 	 * Add the failed BSSID into the blacklist and speed up next scan
 	 * attempt if there could be other APs that could accept association.
-	 * The current blacklist count indicates how many times we have tried
-	 * connecting to this AP and multiple attempts mean that other APs are
-	 * either not available or has already been tried, so that we can start
-	 * increasing the delay here to avoid constant scanning.
 	 */
 	count = wpa_blacklist_add(wpa_s, bssid);
 	if (count == 1 && wpa_s->current_bss) {
@@ -7265,19 +7451,19 @@
 		}
 	}
 
-	/*
-	 * Add previous failure count in case the temporary blacklist was
-	 * cleared due to no other BSSes being available.
-	 */
-	count += wpa_s->extra_blacklist_count;
+	wpa_s->consecutive_conn_failures++;
 
-	if (count > 3 && wpa_s->current_ssid) {
+	if (wpa_s->consecutive_conn_failures > 3 && wpa_s->current_ssid) {
 		wpa_printf(MSG_DEBUG, "Continuous association failures - "
 			   "consider temporary network disabling");
 		wpas_auth_failed(wpa_s, "CONN_FAILED");
 	}
-
-	switch (count) {
+	/*
+	 * Multiple consecutive connection failures mean that other APs are
+	 * either not available or have already been tried, so we can start
+	 * increasing the delay here to avoid constant scanning.
+	 */
+	switch (wpa_s->consecutive_conn_failures) {
 	case 1:
 		timeout = 100;
 		break;
@@ -7295,8 +7481,9 @@
 		break;
 	}
 
-	wpa_dbg(wpa_s, MSG_DEBUG, "Blacklist count %d --> request scan in %d "
-		"ms", count, timeout);
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"Consecutive connection failures: %d --> request scan in %d ms",
+		wpa_s->consecutive_conn_failures, timeout);
 
 	/*
 	 * TODO: if more than one possible AP is available in scan results,
@@ -7695,7 +7882,6 @@
 	wpa_s->normal_scans = 0;
 	wpa_s->scan_req = NORMAL_SCAN_REQ;
 	wpa_supplicant_reinit_autoscan(wpa_s);
-	wpa_s->extra_blacklist_count = 0;
 	wpa_s->disconnected = 0;
 	wpa_s->reassociate = 1;
 	wpa_s->last_owe_group = 0;
@@ -7961,7 +8147,7 @@
 			  ETH_ALEN);
 		num_bssid++;
 	}
-	ret = wpa_drv_set_bssid_blacklist(wpa_s, num_bssid, bssids);
+	ret = wpa_drv_set_bssid_tmp_disallow(wpa_s, num_bssid, bssids);
 	os_free(bssids);
 	return ret;
 }
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 3b90567..c10dd73 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -843,6 +843,16 @@
 # bssid: BSSID (optional); if set, this network block is used only when
 #	associating with the AP using the configured BSSID
 #
+# ignore_broadcast_ssid: SSID broadcast behavior
+# Send empty SSID in beacons and ignore probe request frames that do not
+# specify full SSID, i.e., require stations to know SSID.
+# default: disabled (0)
+# 1 = send empty (length=0) SSID in beacon and ignore probe request for
+#     broadcast SSID
+# 2 = clear SSID (ASCII 0), but keep the original length (this may be required
+#     with some clients that do not support empty SSID) and ignore probe
+#     requests for broadcast SSID
+#
 # priority: priority group (integer)
 # By default, all networks will get same priority group (0). If some of the
 # networks are more desirable, this field can be used to change the order in
@@ -1472,6 +1482,58 @@
 # 2: do not allow PFS to be used
 #dpp_pfs=0
 
+# Whether Beacon protection is enabled
+# This depends on management frame protection (ieee80211w) being enabled.
+#beacon_prot=0
+
+# OWE DH Group
+# 0: use default (19) first and then try all supported groups one by one if AP
+#   rejects the selected group
+# 1-65535: DH Group to use for OWE
+# Groups 19 (NIST P-256), 20 (NIST P-384), and 21 (NIST P-521) are
+# currently supported.
+#owe_group=0
+
+# OWE-only mode (disable transition mode)
+# 0: enable transition mode (allow connection to either OWE or open BSS)
+# 1 = disable transition mode (allow connection only with OWE)
+#owe_only=0
+
+# OWE PTK derivation workaround
+# Initial OWE implementation used SHA256 when deriving the PTK for all
+# OWE groups. This was supposed to change to SHA384 for group 20 and
+# SHA512 for group 21. This parameter can be used to enable older
+# behavior mainly for testing purposes. There is no impact to group 19
+# behavior, but if enabled, this will make group 20 and 21 cases use
+# SHA256-based PTK derivation which will not work with the updated
+# OWE implementation on the AP side.
+#owe_ptk_workaround=0
+
+# Transition Disable indication
+# The AP can notify authenticated stations to disable transition mode
+# in their network profiles when the network has completed transition
+# steps, i.e., once sufficiently large number of APs in the ESS have
+# been updated to support the more secure alternative. When this
+# indication is used, the stations are expected to automatically
+# disable transition mode and less secure security options. This
+# includes use of WEP, TKIP (including use of TKIP as the group
+# cipher), and connections without PMF.
+# Bitmap bits:
+# bit 0 (0x01): WPA3-Personal (i.e., disable WPA2-Personal = WPA-PSK
+#	and only allow SAE to be used)
+# bit 1 (0x02): SAE-PK (disable SAE without use of SAE-PK)
+# bit 2 (0x04): WPA3-Enterprise (move to requiring PMF)
+# bit 3 (0x08): Enhanced Open (disable use of open network; require
+#	OWE)
+
+# SAE-PK mode
+# 0: automatic SAE/SAE-PK selection based on password; enable
+#    transition mode (allow SAE authentication without SAE-PK)
+# 1: SAE-PK only (disable transition mode; allow SAE authentication
+#    only with SAE-PK)
+# 2: disable SAE-PK (allow SAE authentication only without SAE-PK)
+#sae_pk=0
+
 # MAC address policy
 # 0 = use permanent MAC address
 # 1 = use random MAC address for each ESS connection
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 7c792b3..3fcd95a 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -121,6 +121,18 @@
 	 * interface that is not a network interface.
 	 */
 	int p2p_mgmt;
+
+#ifdef CONFIG_MATCH_IFACE
+	/**
+	 * matched - Interface was matched rather than specified
+	 *
+	 */
+	enum {
+		WPA_IFACE_NOT_MATCHED,
+		WPA_IFACE_MATCHED_NULL,
+		WPA_IFACE_MATCHED
+	} matched;
+#endif /* CONFIG_MATCH_IFACE */
 };
 
 /**
@@ -491,6 +503,17 @@
 	int scan_level;
 };
 
+struct robust_av_data {
+	u8 dialog_token;
+	enum scs_request_type request_type;
+	u8 up_bitmap;
+	u8 up_limit;
+	u32 stream_timeout;
+	u8 frame_classifier[48];
+	size_t frame_classifier_len;
+	bool valid_config;
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -643,17 +666,8 @@
 
 	struct wpa_blacklist *blacklist;
 
-	/**
-	 * extra_blacklist_count - Sum of blacklist counts after last connection
-	 *
-	 * This variable is used to maintain a count of temporary blacklisting
-	 * failures (maximum number for any BSS) over blacklist clear
-	 * operations. This is needed for figuring out whether there has been
-	 * failures prior to the last blacklist clear operation which happens
-	 * whenever no other not-blacklisted BSS candidates are available. This
-	 * gets cleared whenever a connection has been established successfully.
-	 */
-	int extra_blacklist_count;
+	/* Number of connection failures since last successful connection */
+	unsigned int consecutive_conn_failures;
 
 	/**
 	 * scan_req - Type of the scan request
@@ -1157,6 +1171,13 @@
 	struct wpabuf *rsnxe_override_assoc;
 	struct wpabuf *rsnxe_override_eapol;
 	struct dl_list drv_signal_override;
+	unsigned int oci_freq_override_eapol;
+	unsigned int oci_freq_override_saquery_req;
+	unsigned int oci_freq_override_saquery_resp;
+	unsigned int oci_freq_override_eapol_g2;
+	unsigned int oci_freq_override_ft_assoc;
+	unsigned int oci_freq_override_fils_assoc;
+	unsigned int oci_freq_override_wnm_sleep;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -1282,10 +1303,10 @@
 	unsigned int dpp_resp_retry_time;
 	u8 dpp_last_ssid[SSID_MAX_LEN];
 	size_t dpp_last_ssid_len;
+	bool dpp_conf_backup_received;
 #ifdef CONFIG_DPP2
 	struct dpp_pfs *dpp_pfs;
 	int dpp_pfs_fallback;
-	struct wpabuf *dpp_reconfig_announcement;
 	struct wpabuf *dpp_presence_announcement;
 	struct dpp_bootstrap_info *dpp_chirp_bi;
 	int dpp_chirp_freq;
@@ -1296,6 +1317,7 @@
 	int dpp_chirp_listen;
 	struct wpa_ssid *dpp_reconfig_ssid;
 	int dpp_reconfig_ssid_id;
+	struct dpp_reconfig_id *dpp_reconfig_id;
 #endif /* CONFIG_DPP2 */
 #ifdef CONFIG_TESTING_OPTIONS
 	char *dpp_config_obj_override;
@@ -1315,6 +1337,8 @@
 	unsigned int multi_ap_ie:1;
 	unsigned int multi_ap_backhaul:1;
 	unsigned int multi_ap_fronthaul:1;
+	struct robust_av_data robust_av;
+	bool mscs_setup_done;
 };
 
 
@@ -1338,6 +1362,8 @@
 const char * wpa_supplicant_state_txt(enum wpa_states state);
 int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_update_bridge_ifname(struct wpa_supplicant *wpa_s,
+					const char *bridge_ifname);
 int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 			      struct wpa_bss *bss, struct wpa_ssid *ssid,
 			      u8 *wpa_ie, size_t *wpa_ie_len);
@@ -1363,6 +1389,7 @@
 
 struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id);
+int wpa_supplicant_remove_all_networks(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
 				   struct wpa_ssid *ssid);
 void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
@@ -1450,6 +1477,7 @@
 void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s);
 void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s);
 void wpas_clear_disabled_interface(void *eloop_ctx, void *timeout_ctx);
+void wpa_supplicant_reset_bgscan(struct wpa_supplicant *wpa_s);
 
 
 /* MBO functions */
@@ -1542,6 +1570,9 @@
 int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s,
 					struct channel_list_changed *info);
+int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
+					   struct wpa_bss *current_bss,
+					   struct wpa_bss *seleceted);
 
 /* eap_register.c */
 int eap_register_methods(void);
@@ -1633,4 +1664,13 @@
 
 void wpas_clear_driver_signal_override(struct wpa_supplicant *wpa_s);
 
+int wpas_send_mscs_req(struct wpa_supplicant *wpa_s);
+void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
+				      struct wpabuf *buf);
+void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
+				       const u8 *src, const u8 *buf,
+				       size_t len);
+void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
+				 const u8 *ies, size_t ies_len);
+
 #endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index fce7e5e..7a558f3 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -6,3 +6,4 @@
 pmf=1
 p2p_add_cli_chan=1
 oce=1
+sae_pwe=2
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 6bd271e..4462afc 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -575,7 +575,8 @@
 				    const u8 *bssid, const u8 *pmkid,
 				    const u8 *fils_cache_id,
 				    const u8 *pmk, size_t pmk_len,
-				    u32 pmk_lifetime, u8 pmk_reauth_threshold)
+				    u32 pmk_lifetime, u8 pmk_reauth_threshold,
+				    int akmp)
 {
 	struct wpa_supplicant *wpa_s = _wpa_s;
 	struct wpa_ssid *ssid;
@@ -583,9 +584,22 @@
 
 	os_memset(&params, 0, sizeof(params));
 	ssid = wpas_get_network_ctx(wpa_s, network_ctx);
-	if (ssid)
+	if (ssid) {
 		wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_ADDED MACSTR " %d",
 			MAC2STR(bssid), ssid->id);
+		if ((akmp == WPA_KEY_MGMT_FT_IEEE8021X ||
+		     akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) &&
+		    !ssid->ft_eap_pmksa_caching) {
+			/* Since we will not be using PMKSA caching for FT-EAP
+			 * within wpa_supplicant to avoid known interop issues
+			 * with APs, do not add this PMKID to the driver either
+			 * so that we won't be hitting those interop issues
+			 * with driver-based RSNE generation. */
+			wpa_printf(MSG_DEBUG,
+				   "FT: Do not add PMKID entry to the driver since FT-EAP PMKSA caching is not enabled in configuration");
+			return 0;
+		}
+	}
 	if (ssid && fils_cache_id) {
 		params.ssid = ssid->ssid;
 		params.ssid_len = ssid->ssid_len;
@@ -1268,6 +1282,7 @@
 	if (!ssid)
 		return;
 
+#ifdef CONFIG_SAE
 	if ((bitmap & TRANSITION_DISABLE_WPA3_PERSONAL) &&
 	    wpa_key_mgmt_sae(wpa_s->key_mgmt) &&
 	    (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) &&
@@ -1279,6 +1294,24 @@
 		changed = 1;
 	}
 
+	if ((bitmap & TRANSITION_DISABLE_SAE_PK) &&
+	    wpa_key_mgmt_sae(wpa_s->key_mgmt) &&
+#ifdef CONFIG_SME
+	    wpa_s->sme.sae.state == SAE_ACCEPTED &&
+	    wpa_s->sme.sae.pk &&
+#endif /* CONFIG_SME */
+	    (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) &&
+	    (ssid->sae_pk != SAE_PK_MODE_ONLY ||
+	     ssid->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED ||
+	     (ssid->group_cipher & WPA_CIPHER_TKIP))) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE-PK: SAE authentication without PK disabled based on AP notification");
+		disable_wpa_wpa2(ssid);
+		ssid->sae_pk = SAE_PK_MODE_ONLY;
+		changed = 1;
+	}
+#endif /* CONFIG_SAE */
+
 	if ((bitmap & TRANSITION_DISABLE_WPA3_ENTERPRISE) &&
 	    wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
 	    (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X |
@@ -1298,6 +1331,8 @@
 		changed = 1;
 	}
 
+	wpas_notify_transition_disable(wpa_s, ssid, bitmap);
+
 	if (!changed)
 		return;
 
diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c
index 4e37591..1c136f7 100644
--- a/wpa_supplicant/wpas_module_tests.c
+++ b/wpa_supplicant/wpas_module_tests.c
@@ -59,6 +59,23 @@
 	    wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0)
 		goto fail;
 
+	wpa_blacklist_clear(&wpa_s);
+
+	if (wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0 ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "444444") < 0 ||
+	    !wpa_blacklist_is_blacklisted(&wpa_s, (u8 *) "111111") ||
+	    wpa_blacklist_del(&wpa_s, (u8 *) "111111") < 0 ||
+	    wpa_blacklist_is_blacklisted(&wpa_s, (u8 *) "111111") ||
+	    wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0)
+		goto fail;
+
+	wpa_blacklist_update(&wpa_s);
+
+	if (!wpa_blacklist_is_blacklisted(&wpa_s, (u8 *) "111111"))
+		goto fail;
+
 	ret = 0;
 fail:
 	wpa_blacklist_clear(&wpa_s);
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 9f68b22..47d85dc 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -720,7 +720,7 @@
 	wpas_notify_wps_event_success(wpa_s);
 	if (wpa_s->current_ssid)
 		wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1);
-	wpa_s->extra_blacklist_count = 0;
+	wpa_s->consecutive_conn_failures = 0;
 
 	/*
 	 * Enable the networks disabled during wpas_wps_reassoc after 10