Cumulative patch from commit ee48f48ba

The following commits are merged as well as changes in
./wpa_supplicant/hidl/1.3 to fix compilation errors.

ee48f48ba hostapd: Support showing neighbor list through hostapd_cli
3204795d7 STA OBSS: Add check for overlapping BSSs
3af78a4e0 Extract BSS coex 40 MHz check into a separate function
70755e658 Allow removing neighbor DB entries by BSSID alone
bf3ab50f4 Change some RRM debug messages from wpa_printf() to wpa_dbg()
a737e40b5 drivers: Support of dynamic VLAN requires Linux ioctls
9b391715c mesh: Allow group management cipher to be configured
65d0b67c2 mesh: Increase plink action frame AMPY buffer length for max GTK/IGTK
b1b62a136 WPS: Add WPS-PIN-ACTIVE and WPS-CANCEL events
0b1839405 Add "reconnect" cmdline argument to hostapd_cli/wpa_cli
4318a635a DBus: Update dont_quote[] with new network profile parameters
26a00ba8b DBus: Remove not existing network profile params from dont_quote[]
23d87687c dbus: Move roam metrics to the correct interface
8e111157e nl80211: Relax bridge setup
3626e72c8 l2_packet: Fix bridge workaround for repeater configuration
84972999b FT: More debug prints for RSNE modification for EAPOL-Key msg 2/4
4cfa8b92b Fix wpa_insert_pmkid() when buffer includes extra IEs
32289112c wpa_passphrase: Output errors to stderr
07fe134d9 EAP-SIM peer: Do not accept SIM/Challenge without SIM/Start
b2e2a8588 OpenSSL: Extend key_block size determination to support GCM/CCM ciphers
efaa6256e More detailed documentation on ieee80211w configuration parameter
1730a6a5e nl80211: Fix couple of typos in a comment
307cfc328 Strip trailing zero data in EAPOL-Key msg 1/4 when no PMKID to send
2c5ccfa60 nl80211: Initialize full channel info struct even if channel is not known
e6d3aca9c WPS: Add prefixes to public event_* functions
2ba6aa604 Fix memory leak in case allocation of token fails during JSON parsing
84877f253 wpa_supplicant: Do not try to detect PSK mismatch during PTK rekeying
78d338d1b DPP: Fix a memory leak on an error path
9be30ffc1 DPP: Abort ongoing scan if connection status needs to be sent
3f3876344 SAE: Enable NEED_DH_GROUPS_ALL for CONFIG_TESTING_OPTIONS=y builds
1f1567d2a QCA vendor command for getting STA information
e5620bf02 6 GHz: Select channel width using configured op_class
0bfc04b8d Do not enable HT/VHT when operating in 6 GHz band
da8570f4c Allow non-PCS 6 GHz channels to be excluded from ACS
59bb72642 Allow ACS channel list to be configured as frequencies (in MHz)
5f9b4afdf Use frequency in HT/VHT validation steps done before starting AP
59e33b4a9 ACS: Select current hw_mode based on the selected frequency
bb781c763 AP: Populate iface->freq before starting AP
41cac481a ACS: Use frequency params in ACS (offload) completed event interface
840532aea Search through all hw_features sets in hw_get_channel_freq()
15d356873 ACS: Add channels from all modes matching with configured hw mode
996662250 P2P: Add support for EDMG channels
f86e34168 Update operating classes and channels for the 60 GHz band
a19277a28 Add EDMG bandwidth to channel frequency APIs
5a563a3d3 Avoid compiler warning on shadowing a local variable
e3c476bd8 SAE H2E: Fix RSNXE override in EAPOL-Key msg 2/4 for testing purposes
7fde39fb1 Add sae_rejected_groups to hostapd STA control interface command
5661ebd77 DPP: Configurator netRole for Enrollee
740457445 DPP: Replace ap boolean with netRole enum in Configurator params
f2c4b44b4 SAE H2E: RSNXE override in EAPOL-Key msg 3/4
132565539 SAE H2E: RSNXE override for testing purposes
a889e9a70 SAE: Reject invalid rejected group report in SAE commit explicitly (AP)
c88e01e1b SAE H2E: Fix validation of rejected groups list
b834e9700 SAE H2E: Testing option to report extra rejected groups
918df2227 SAE: Reject unexpected Status Code in SAE commit explicitly (AP)
405946d76 SAE: Testing option to ignore H2E requirement mismatch
23acdd9f3 Fix memory leak in ACS offload operation
e86ba912a ACS: Remove redundant ch_list parameters from do_acs interface
3cf360b8e DFS: Don't handle DFS ops for 6 GHz channels
ee0030e81 6 GHz: Do not check for HT capability on 6 GHz channels
e780b4bf2 DPP: Bootstrapping via NFC URI Record
3c0d6eb8a Sync with mac80211-next.git include/uapi/linux/nl80211.h
38203148e Extend hostapd to support setband to driver via QCA vendor command
3c13af557 hostapd: Register wpa_msg callback even if only global ctrl_iface is used
e92080568 hostapd: Extend global control interface notifications
9f50538e1 SAE H2E: Do not use sae_h2e param in AP mode if SAE is disabled
181bf9336 DPP2: Report received ssid_charset as DPP-CONFOBJ-SSID-CHARSET event
71e2848ea DPP2: ssid64/ssid_charset in Configurator
57a63b131 DPP2: Add parsing of ssid64/ssid_charset in Config Object
590bc64d6 DPP: Use JSON token builder helpers
e75ab5322 JSON: Add helper functions for building tokens
8e5e36a18 Clean up base64_{encode,decode} pointer types
b22608423 Clean up base64_url_{encode,decode} pointer types
a4255a207 Simplify base64_url_encode() prototype
c54227c26 DPP2: Replace connectionStatus object ssid with ssid64
31b6eb417 tests: Update SAE H2E test case to match SSWU parameter z change
0432237a4 SAE H2E: Update SSWU curve-specific parameter z values
5b50265e1 WMM: Do not modify input TSPEC buffer during processing
783a99b73 HS2.0: Skip check for roaming_consortium for NAI realm query
f3c077929 P2P: Fix listen state machine getting stuck in send_action() scheduled case
530b8ee3c hostapd: Update DFS status in VHT80+80 mode
93ba13bcf Fix status code in SAE/DPP association PMKID mismatch (driver-AP-SME)
b90d2f7ff Fix a typo in a comment

Bug: 146900007
Test: Device boots up and connects to wifi networks, run traffic.
Test: Able to turn on/off softap, associate wifi STA, run traffic.
Test: Regression test Passed (Bug: 146905211)
Change-Id: Idc3584b491429a2e4935068a293bff572fc9d62e
diff --git a/src/ap/acs.c b/src/ap/acs.c
index f12539f..232afa8 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -862,6 +862,7 @@
 	}
 
 	iface->conf->channel = ideal_chan->chan;
+	iface->freq = ideal_chan->freq;
 
 	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
 		acs_adjust_center_freq(iface);
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 58fc3e9..68af3c1 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -441,7 +441,7 @@
 	struct hostapd_ssid *ssid = &conf->ssid;
 	struct sae_password_entry *pw;
 
-	if (conf->sae_pwe == 0)
+	if (conf->sae_pwe == 0 || !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
 		return 0; /* PT not needed */
 
 	sae_deinit_pt(ssid->pt);
@@ -880,6 +880,7 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	wpabuf_free(conf->own_ie_override);
 	wpabuf_free(conf->sae_commit_override);
+	wpabuf_free(conf->rsnxe_override_eapol);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	os_free(conf->no_probe_resp_if_seen_on);
@@ -935,6 +936,7 @@
 	os_free(conf->supported_rates);
 	os_free(conf->basic_rates);
 	os_free(conf->acs_ch_list.range);
+	os_free(conf->acs_freq_list.range);
 	os_free(conf->driver_params);
 #ifdef CONFIG_ACS
 	os_free(conf->acs_chan_bias);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 2a0c984..83b8aee 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -664,6 +664,7 @@
 	struct wpabuf *own_ie_override;
 	int sae_reflection_attack;
 	struct wpabuf *sae_commit_override;
+	struct wpabuf *rsnxe_override_eapol;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #define MESH_ENABLED BIT(0)
@@ -881,8 +882,11 @@
 	u8 edmg_channel;
 	u8 acs;
 	struct wpa_freq_range_list acs_ch_list;
+	struct wpa_freq_range_list acs_freq_list;
+	u8 acs_freq_list_present;
 	int acs_exclude_dfs;
 	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+	int acs_exclude_6ghz_non_psc;
 	enum {
 		LONG_PREAMBLE = 0,
 		SHORT_PREAMBLE = 1
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 204274f..7585866 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "common/hw_features_common.h"
 #include "wps/wps.h"
 #include "p2p/p2p.h"
@@ -855,10 +856,24 @@
 	for (i = 0; i < mode->num_channels; i++) {
 		struct hostapd_channel_data *chan = &mode->channels[i];
 
-		if ((acs_ch_list_all ||
-		     freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
-					      chan->chan)) &&
-		    !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		if (!acs_ch_list_all &&
+		    (hapd->iface->conf->acs_freq_list.num &&
+		     !freq_range_list_includes(
+			     &hapd->iface->conf->acs_freq_list,
+			     chan->freq)))
+			continue;
+		if (!acs_ch_list_all &&
+		    (!hapd->iface->conf->acs_freq_list_present &&
+		     hapd->iface->conf->acs_ch_list.num &&
+		     !freq_range_list_includes(
+			     &hapd->iface->conf->acs_ch_list,
+			     chan->chan)))
+			continue;
+		if (is_6ghz_freq(chan->freq) &&
+		    hapd->iface->conf->acs_exclude_6ghz_non_psc &&
+		    !is_6ghz_psc_frequency(chan->freq))
+			continue;
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
 		    !(hapd->iface->conf->acs_exclude_dfs &&
 		      (chan->flag & HOSTAPD_CHAN_RADAR)))
 			int_array_add_unique(freq_list, chan->freq);
@@ -884,10 +899,9 @@
 {
 	struct drv_acs_params params;
 	int ret, i, acs_ch_list_all = 0;
-	u8 *channels = NULL;
-	unsigned int num_channels = 0;
 	struct hostapd_hw_modes *mode;
 	int *freq_list = NULL;
+	enum hostapd_hw_mode selected_mode;
 
 	if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
 		return 0;
@@ -899,41 +913,25 @@
 	 * If no chanlist config parameter is provided, include all enabled
 	 * channels of the selected hw_mode.
 	 */
-	if (!hapd->iface->conf->acs_ch_list.num)
-		acs_ch_list_all = 1;
+	if (hapd->iface->conf->acs_freq_list_present)
+		acs_ch_list_all = !hapd->iface->conf->acs_freq_list.num;
+	else
+		acs_ch_list_all = !hapd->iface->conf->acs_ch_list.num;
 
-	mode = hapd->iface->current_mode;
-	if (mode) {
-		channels = os_malloc(mode->num_channels);
-		if (channels == NULL)
-			return -1;
+	if (hapd->iface->current_mode)
+		selected_mode = hapd->iface->current_mode->mode;
+	else
+		selected_mode = HOSTAPD_MODE_IEEE80211ANY;
 
-		for (i = 0; i < mode->num_channels; i++) {
-			struct hostapd_channel_data *chan = &mode->channels[i];
-			if (!acs_ch_list_all &&
-			    !freq_range_list_includes(
-				    &hapd->iface->conf->acs_ch_list,
-				    chan->chan))
-				continue;
-			if (hapd->iface->conf->acs_exclude_dfs &&
-			    (chan->flag & HOSTAPD_CHAN_RADAR))
-				continue;
-			if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
-				channels[num_channels++] = chan->chan;
-				int_array_add_unique(&freq_list, chan->freq);
-			}
-		}
-	} else {
-		for (i = 0; i < hapd->iface->num_hw_features; i++) {
-			mode = &hapd->iface->hw_features[i];
-			hostapd_get_hw_mode_any_channels(hapd, mode,
-							 acs_ch_list_all,
-							 &freq_list);
-		}
+	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+		mode = &hapd->iface->hw_features[i];
+		if (selected_mode != HOSTAPD_MODE_IEEE80211ANY &&
+		    selected_mode != mode->mode)
+			continue;
+		hostapd_get_hw_mode_any_channels(hapd, mode, acs_ch_list_all,
+						 &freq_list);
 	}
 
-	params.ch_list = channels;
-	params.ch_list_len = num_channels;
 	params.freq_list = freq_list;
 
 	params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
@@ -958,8 +956,11 @@
 			params.ch_width = 160;
 	}
 
+	if (hapd->iface->conf->op_class)
+		params.ch_width = op_class_to_bandwidth(
+			hapd->iface->conf->op_class);
 	ret = hapd->driver->do_acs(hapd->drv_priv, &params);
-	os_free(channels);
+	os_free(freq_list);
 
 	return ret;
 }
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 79b1302..fa413df 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -382,4 +382,12 @@
 	return hapd->driver->send_external_auth_status(hapd->drv_priv, params);
 }
 
+static inline int
+hostapd_drv_set_band(struct hostapd_data *hapd, enum set_band band)
+{
+	if (!hapd->driver || !hapd->drv_priv || !hapd->driver->set_band)
+		return -1;
+	return hapd->driver->set_band(hapd->drv_priv, band);
+}
+
 #endif /* AP_DRV_OPS */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index bde61ee..9fd1b81 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -273,6 +273,36 @@
 		if (!os_snprintf_error(buflen - len, res))
 			len += res;
 	}
+
+	if (sta->sae && sta->sae->tmp) {
+		const u8 *pos;
+		unsigned int j, count;
+		struct wpabuf *groups = sta->sae->tmp->peer_rejected_groups;
+
+		res = os_snprintf(buf + len, buflen - len,
+				  "sae_rejected_groups=");
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+
+		if (groups) {
+			pos = wpabuf_head(groups);
+			count = wpabuf_len(groups) / 2;
+		} else {
+			pos = NULL;
+			count = 0;
+		}
+		for (j = 0; pos && j < count; j++) {
+			res = os_snprintf(buf + len, buflen - len, "%s%d",
+					  j == 0 ? "" : " ", WPA_GET_LE16(pos));
+			if (!os_snprintf_error(buflen - len, res))
+				len += res;
+			pos += 2;
+		}
+
+		res = os_snprintf(buf + len, buflen - len, "\n");
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
 #endif /* CONFIG_SAE */
 
 	if (sta->vlan_id > 0) {
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index c4c00fc..f70ecc9 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -515,6 +515,7 @@
 	int n_chans = 1, i;
 	struct hostapd_hw_modes *mode;
 	int frequency = freq;
+	int frequency2 = 0;
 	int ret = 0;
 
 	mode = iface->current_mode;
@@ -542,6 +543,11 @@
 		n_chans = 4;
 		frequency = cf1 - 30;
 		break;
+	case CHAN_WIDTH_80P80:
+		n_chans = 4;
+		frequency = cf1 - 30;
+		frequency2 = cf2 - 30;
+		break;
 	case CHAN_WIDTH_160:
 		n_chans = 8;
 		frequency = cf1 - 70;
@@ -557,6 +563,11 @@
 	for (i = 0; i < n_chans; i++) {
 		ret += set_dfs_state_freq(iface, frequency, state);
 		frequency = frequency + 20;
+
+		if (chan_width == CHAN_WIDTH_80P80) {
+			ret += set_dfs_state_freq(iface, frequency2, state);
+			frequency2 = frequency2 + 20;
+		}
 	}
 
 	return ret;
@@ -662,6 +673,9 @@
 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
 	int skip_radar = 0;
 
+	if (is_6ghz_freq(iface->freq))
+		return 1;
+
 	if (!iface->current_mode) {
 		/*
 		 * This can happen with drivers that do not provide mode
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 085d423..1a3a815 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -1,6 +1,7 @@
 /*
  * hostapd / DPP integration
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -62,6 +63,24 @@
 }
 
 
+/**
+ * hostapd_dpp_nfc_uri - Parse and add DPP bootstrapping info from NFC Tag (URI)
+ * @hapd: Pointer to hostapd_data
+ * @cmd: DPP URI read from a NFC Tag (URI NDEF message)
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, cmd);
+	if (!bi)
+		return -1;
+
+	return bi->id;
+}
+
+
 static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
 						void *timeout_ctx)
 {
@@ -768,7 +787,8 @@
 	struct wpabuf *buf;
 	int res;
 
-	buf = dpp_build_conf_req_helper(auth, hapd->conf->dpp_name, 1,
+	buf = dpp_build_conf_req_helper(auth, hapd->conf->dpp_name,
+					DPP_NETROLE_AP,
 					hapd->conf->dpp_mud_url, NULL);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index c1ec5d7..e151c2f 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -1,6 +1,7 @@
 /*
  * hostapd / DPP integration
  * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +11,7 @@
 #define DPP_HOSTAPD_H
 
 int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
 void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 3198bd5..8a4b0ef 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -17,6 +17,7 @@
 #include "common/wpa_ctrl.h"
 #include "common/dpp.h"
 #include "common/sae.h"
+#include "common/hw_features_common.h"
 #include "crypto/random.h"
 #include "p2p/p2p.h"
 #include "wps/wps.h"
@@ -344,6 +345,9 @@
 			} else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
 				reason = WLAN_REASON_CIPHER_SUITE_REJECTED;
 				status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+			} else if (res == WPA_INVALID_PMKID) {
+				reason = WLAN_REASON_INVALID_PMKID;
+				status = WLAN_STATUS_INVALID_PMKID;
 			} else {
 				reason = WLAN_REASON_INVALID_IE;
 				status = WLAN_STATUS_INVALID_IE;
@@ -932,6 +936,7 @@
 {
 	int ret, i;
 	int err = 0;
+	struct hostapd_channel_data *pri_chan;
 
 	if (hapd->iconf->channel) {
 		wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
@@ -939,12 +944,20 @@
 		return;
 	}
 
+	hapd->iface->freq = acs_res->pri_freq;
+
 	if (!hapd->iface->current_mode) {
 		for (i = 0; i < hapd->iface->num_hw_features; i++) {
 			struct hostapd_hw_modes *mode =
 				&hapd->iface->hw_features[i];
 
 			if (mode->mode == acs_res->hw_mode) {
+				if (hapd->iface->freq > 0 &&
+				    !hw_get_chan(mode->mode,
+						 hapd->iface->freq,
+						 hapd->iface->hw_features,
+						 hapd->iface->num_hw_features))
+					continue;
 				hapd->iface->current_mode = mode;
 				break;
 			}
@@ -958,24 +971,33 @@
 		}
 	}
 
-	hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel);
-
-	if (!acs_res->pri_channel) {
+	if (!acs_res->pri_freq) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_WARNING,
 			       "driver switched to bad channel");
 		err = 1;
 		goto out;
 	}
+	pri_chan = hw_get_channel_freq(hapd->iface->current_mode->mode,
+				       acs_res->pri_freq, NULL,
+				       hapd->iface->hw_features,
+				       hapd->iface->num_hw_features);
+	if (!pri_chan) {
+		wpa_printf(MSG_ERROR,
+			   "ACS: Could not determine primary channel number from pri_freq %u",
+			   acs_res->pri_freq);
+		err = 1;
+		goto out;
+	}
 
-	hapd->iconf->channel = acs_res->pri_channel;
+	hapd->iconf->channel = pri_chan->chan;
 	hapd->iconf->acs = 1;
 
-	if (acs_res->sec_channel == 0)
+	if (acs_res->sec_freq == 0)
 		hapd->iconf->secondary_channel = 0;
-	else if (acs_res->sec_channel < acs_res->pri_channel)
+	else if (acs_res->sec_freq < acs_res->pri_freq)
 		hapd->iconf->secondary_channel = -1;
-	else if (acs_res->sec_channel > acs_res->pri_channel)
+	else if (acs_res->sec_freq > acs_res->pri_freq)
 		hapd->iconf->secondary_channel = 1;
 	else {
 		wpa_printf(MSG_ERROR, "Invalid secondary channel!");
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 3686438..e4950e3 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1572,6 +1572,51 @@
 }
 
 
+static int configured_fixed_chan_to_freq(struct hostapd_iface *iface)
+{
+	int freq, i, j;
+
+	if (!iface->conf->channel)
+		return 0;
+	if (iface->conf->op_class) {
+		freq = ieee80211_chan_to_freq(NULL, iface->conf->op_class,
+					      iface->conf->channel);
+		if (freq < 0) {
+			wpa_printf(MSG_INFO,
+				   "Could not convert op_class %u channel %u to operating frequency",
+				   iface->conf->op_class, iface->conf->channel);
+			return -1;
+		}
+		iface->freq = freq;
+		return 0;
+	}
+
+	/* Old configurations using only 2.4/5/60 GHz bands may not specify the
+	 * op_class parameter. Select a matching channel from the configured
+	 * mode using the channel parameter for these cases.
+	 */
+	for (j = 0; j < iface->num_hw_features; j++) {
+		struct hostapd_hw_modes *mode = &iface->hw_features[j];
+
+		if (iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY &&
+		    iface->conf->hw_mode != mode->mode)
+			continue;
+		for (i = 0; i < mode->num_channels; i++) {
+			struct hostapd_channel_data *chan = &mode->channels[i];
+
+			if (chan->chan == iface->conf->channel &&
+			    !is_6ghz_freq(chan->freq)) {
+				iface->freq = chan->freq;
+				return 0;
+			}
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Could not determine operating frequency");
+	return -1;
+}
+
+
 static int setup_interface2(struct hostapd_iface *iface)
 {
 	iface->wait_channel_update = 0;
@@ -1580,7 +1625,20 @@
 		/* Not all drivers support this yet, so continue without hw
 		 * feature data. */
 	} else {
-		int ret = hostapd_select_hw_mode(iface);
+		int ret;
+
+		ret = configured_fixed_chan_to_freq(iface);
+		if (ret < 0)
+			goto fail;
+
+		if (iface->conf->op_class) {
+			int ch_width;
+
+			ch_width = op_class_to_ch_width(iface->conf->op_class);
+			hostapd_set_oper_chwidth(iface->conf, ch_width);
+		}
+
+		ret = hostapd_select_hw_mode(iface);
 		if (ret < 0) {
 			wpa_printf(MSG_ERROR, "Could not select hw_mode and "
 				   "channel. (%d)", ret);
@@ -1864,12 +1922,11 @@
 		goto fail;
 
 	wpa_printf(MSG_DEBUG, "Completing interface initialization");
-	if (iface->conf->channel) {
+	if (iface->freq) {
 #ifdef NEED_AP_MLME
 		int res;
 #endif /* NEED_AP_MLME */
 
-		iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel);
 		wpa_printf(MSG_DEBUG, "Mode: %s  Channel: %d  "
 			   "Frequency: %d MHz",
 			   hostapd_hw_mode_txt(iface->conf->hw_mode),
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 2fefaf8..ba10752 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -227,13 +227,25 @@
 #ifdef CONFIG_IEEE80211N
 static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
 {
-	int pri_chan, sec_chan;
+	int pri_freq, sec_freq;
+	struct hostapd_channel_data *p_chan, *s_chan;
 
-	pri_chan = iface->conf->channel;
-	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
+	pri_freq = iface->freq;
+	sec_freq = pri_freq + iface->conf->secondary_channel * 20;
 
-	return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
-					 sec_chan);
+	if (!iface->current_mode)
+		return 0;
+
+	p_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, NULL,
+				     iface->hw_features,
+				     iface->num_hw_features);
+
+	s_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, NULL,
+				     iface->hw_features,
+				     iface->num_hw_features);
+
+	return allowed_ht40_channel_pair(iface->current_mode->mode,
+					 p_chan, s_chan);
 }
 
 
@@ -241,9 +253,11 @@
 {
 	if (iface->conf->secondary_channel > 0) {
 		iface->conf->channel += 4;
+		iface->freq += 20;
 		iface->conf->secondary_channel = -1;
 	} else {
 		iface->conf->channel -= 4;
+		iface->freq -= 20;
 		iface->conf->secondary_channel = 1;
 	}
 }
@@ -252,13 +266,23 @@
 static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
 				     struct wpa_scan_results *scan_res)
 {
-	int pri_chan, sec_chan;
+	unsigned int pri_freq, sec_freq;
 	int res;
+	struct hostapd_channel_data *pri_chan, *sec_chan;
 
-	pri_chan = iface->conf->channel;
-	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
+	pri_freq = iface->freq;
+	sec_freq = pri_freq + iface->conf->secondary_channel * 20;
 
-	res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
+	if (!iface->current_mode)
+		return 0;
+	pri_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq,
+				       NULL, iface->hw_features,
+				       iface->num_hw_features);
+	sec_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq,
+				       NULL, iface->hw_features,
+				       iface->num_hw_features);
+
+	res = check_40mhz_5g(scan_res, pri_chan, sec_chan);
 
 	if (res == 2) {
 		if (iface->conf->no_pri_sec_switch) {
@@ -352,7 +376,7 @@
 	if (iface->current_mode == NULL)
 		return;
 
-	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+	pri_freq = iface->freq;
 	if (iface->conf->secondary_channel > 0)
 		sec_freq = pri_freq + 20;
 	else
@@ -397,7 +421,7 @@
 	if (iface->current_mode == NULL)
 		return;
 
-	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+	pri_freq = iface->freq;
 	if (iface->conf->secondary_channel > 0) {
 		affected_start = pri_freq - 10;
 		affected_end = pri_freq + 30;
@@ -670,6 +694,9 @@
 {
 #ifdef CONFIG_IEEE80211N
 	int ret;
+
+	if (is_6ghz_freq(iface->freq))
+		return 0;
 	if (!iface->conf->ieee80211n)
 		return 0;
 
@@ -731,14 +758,15 @@
 
 
 static int hostapd_is_usable_chan(struct hostapd_iface *iface,
-				  int channel, int primary)
+				  int frequency, int primary)
 {
 	struct hostapd_channel_data *chan;
 
 	if (!iface->current_mode)
 		return 0;
 
-	chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
+	chan = hw_get_channel_freq(iface->current_mode->mode, frequency, NULL,
+				   iface->hw_features, iface->num_hw_features);
 	if (!chan)
 		return 0;
 
@@ -747,8 +775,8 @@
 		return 1;
 
 	wpa_printf(MSG_INFO,
-		   "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
-		   channel, primary ? "primary" : "secondary",
+		   "Frequency %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
+		   frequency, primary ? "primary" : "secondary",
 		   chan->flag,
 		   chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
 		   chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
@@ -762,19 +790,28 @@
 	int num_of_enabled = 0;
 	int max_contiguous = 0;
 	struct ieee80211_edmg_config edmg;
+	struct hostapd_channel_data *pri_chan;
 
 	if (!iface->conf->enable_edmg)
 		return 1;
 
+	if (!iface->current_mode)
+		return 0;
+	pri_chan = hw_get_channel_freq(iface->current_mode->mode,
+				       iface->freq, NULL,
+				       iface->hw_features,
+				       iface->num_hw_features);
 	hostapd_encode_edmg_chan(iface->conf->enable_edmg,
 				 iface->conf->edmg_channel,
-				 iface->conf->channel,
+				 pri_chan->chan,
 				 &edmg);
-	if (!(edmg.channels & BIT(iface->conf->channel - 1)))
+	if (!(edmg.channels & BIT(pri_chan->chan - 1)))
 		return 0;
 
 	/* 60 GHz channels 1..6 */
 	for (i = 0; i < 6; i++) {
+		int freq = 56160 + 2160 * i;
+
 		if (edmg.channels & BIT(i)) {
 			contiguous++;
 			num_of_enabled++;
@@ -789,7 +826,7 @@
 		if (num_of_enabled > 4)
 			return 0;
 
-		if (!hostapd_is_usable_chan(iface, i + 1, 1))
+		if (!hostapd_is_usable_chan(iface, freq, 1))
 			return 0;
 
 		if (contiguous > max_contiguous)
@@ -819,17 +856,23 @@
 
 static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 {
-	int secondary_chan;
+	int secondary_freq;
 	struct hostapd_channel_data *pri_chan;
 
-	pri_chan = hw_get_channel_chan(iface->current_mode,
-				       iface->conf->channel, NULL);
-	if (!pri_chan)
+	if (!iface->current_mode)
 		return 0;
-
-	if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
+	pri_chan = hw_get_channel_freq(iface->current_mode->mode,
+				       iface->freq, NULL,
+				       iface->hw_features,
+				       iface->num_hw_features);
+	if (!pri_chan) {
+		wpa_printf(MSG_ERROR, "Primary frequency not present");
 		return 0;
-
+	}
+	if (!hostapd_is_usable_chan(iface, pri_chan->freq, 1)) {
+		wpa_printf(MSG_ERROR, "Primary frequency not allowed");
+		return 0;
+	}
 	if (!hostapd_is_usable_edmg(iface))
 		return 0;
 
@@ -838,19 +881,19 @@
 
 	if (!iface->conf->ht40_plus_minus_allowed)
 		return hostapd_is_usable_chan(
-			iface, iface->conf->channel +
-			iface->conf->secondary_channel * 4, 0);
+			iface,
+			iface->freq + iface->conf->secondary_channel * 20, 0);
 
 	/* Both HT40+ and HT40- are set, pick a valid secondary channel */
-	secondary_chan = iface->conf->channel + 4;
-	if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+	secondary_freq = iface->freq + 20;
+	if (hostapd_is_usable_chan(iface, secondary_freq, 0) &&
 	    (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
 		iface->conf->secondary_channel = 1;
 		return 1;
 	}
 
-	secondary_chan = iface->conf->channel - 4;
-	if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+	secondary_freq = iface->freq - 20;
+	if (hostapd_is_usable_chan(iface, secondary_freq, 0) &&
 	    (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
 		iface->conf->secondary_channel = -1;
 		return 1;
@@ -863,7 +906,7 @@
 static enum hostapd_chan_status
 hostapd_check_chans(struct hostapd_iface *iface)
 {
-	if (iface->conf->channel) {
+	if (iface->freq) {
 		if (hostapd_is_usable_chans(iface))
 			return HOSTAPD_CHAN_VALID;
 		else
@@ -897,9 +940,9 @@
 	hostapd_logger(iface->bss[0], NULL,
 		       HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_WARNING,
-		       "Configured channel (%d) not found from the "
-		       "channel list of current mode (%d) %s",
+		       "Configured channel (%d) or frequency (%d) not found from the channel list of the current mode (%d) %s",
 		       iface->conf->channel,
+		       iface->freq,
 		       iface->current_mode->mode,
 		       hostapd_hw_mode_txt(iface->current_mode->mode));
 	hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
@@ -919,9 +962,7 @@
 	case HOSTAPD_CHAN_VALID:
 		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
 			ACS_EVENT_COMPLETED "freq=%d channel=%d",
-			hostapd_hw_get_freq(iface->bss[0],
-					    iface->conf->channel),
-			iface->conf->channel);
+			iface->freq, iface->conf->channel);
 		break;
 	case HOSTAPD_CHAN_ACS:
 		wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
@@ -961,7 +1002,6 @@
 int hostapd_select_hw_mode(struct hostapd_iface *iface)
 {
 	int i;
-	int freq = -1;
 
 	if (iface->num_hw_features < 1)
 		return -1;
@@ -978,13 +1018,13 @@
 	}
 
 	iface->current_mode = NULL;
-	if (iface->conf->channel && iface->conf->op_class)
-		freq = ieee80211_chan_to_freq(NULL, iface->conf->op_class,
-					      iface->conf->channel);
 	for (i = 0; i < iface->num_hw_features; i++) {
 		struct hostapd_hw_modes *mode = &iface->hw_features[i];
 		if (mode->mode == iface->conf->hw_mode) {
-			if (freq > 0 && !hw_get_chan(mode, freq))
+			if (iface->freq > 0 &&
+			    !hw_get_chan(mode->mode, iface->freq,
+					 iface->hw_features,
+					 iface->num_hw_features))
 				continue;
 			iface->current_mode = mode;
 			break;
@@ -1048,7 +1088,9 @@
 	struct hostapd_hw_modes *mode;
 
 	if (hapd->iface->current_mode) {
-		channel = hw_get_chan(hapd->iface->current_mode, freq);
+		channel = hw_get_chan(hapd->iface->current_mode->mode, freq,
+				      hapd->iface->hw_features,
+				      hapd->iface->num_hw_features);
 		if (channel)
 			return channel;
 	}
@@ -1059,7 +1101,9 @@
 		return 0;
 	for (i = 0; i < hapd->iface->num_hw_features; i++) {
 		mode = &hapd->iface->hw_features[i];
-		channel = hw_get_chan(mode, freq);
+		channel = hw_get_chan(mode->mode, freq,
+				      hapd->iface->hw_features,
+				      hapd->iface->num_hw_features);
 		if (channel)
 			return channel;
 	}
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 4d30bae..5b70425 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -98,7 +98,8 @@
 		num++;
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 		num++;
-	if (hapd->conf->sae_pwe == 1)
+	if (hapd->conf->sae_pwe == 1 &&
+	    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt))
 		num++;
 	if (num > 8) {
 		/* rest of the rates are encoded in Extended supported
@@ -126,7 +127,9 @@
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
 	}
 
-	if (hapd->conf->sae_pwe == 1 && count < 8) {
+	if (hapd->conf->sae_pwe == 1 &&
+	    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+	    count < 8) {
 		count++;
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
 	}
@@ -148,7 +151,8 @@
 		num++;
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 		num++;
-	if (hapd->conf->sae_pwe == 1)
+	if (hapd->conf->sae_pwe == 1 &&
+	    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt))
 		num++;
 	if (num <= 8)
 		return eid;
@@ -179,7 +183,8 @@
 			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
 	}
 
-	if (hapd->conf->sae_pwe == 1) {
+	if (hapd->conf->sae_pwe == 1 &&
+	    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) {
 		count++;
 		if (count > 8)
 			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
@@ -1117,8 +1122,10 @@
 	if (!sta->sae) {
 		if (auth_transaction != 1 ||
 		    !sae_status_success(hapd, status_code)) {
-			resp = -1;
-			goto remove_sta;
+			wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u",
+				   status_code);
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto reply;
 		}
 		sta->sae = os_zalloc(sizeof(*sta->sae));
 		if (!sta->sae) {
@@ -1273,9 +1280,9 @@
 
 		if (sta->sae->tmp &&
 		    check_sae_rejected_groups(
-			    hapd, sta->sae->tmp->peer_rejected_groups) < 0) {
+			    hapd, sta->sae->tmp->peer_rejected_groups)) {
 			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			goto remove_sta;
+			goto reply;
 		}
 
 		if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) {
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 0b828e9..1e1cc38 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -1011,6 +1011,7 @@
 	u8 *pos = eid;
 
 	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) ||
 	    len < 3)
 		return pos;
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 5415443..4012ae4 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -34,6 +34,60 @@
 }
 
 
+int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+	struct hostapd_neighbor_entry *nr;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		int ret;
+		char nrie[2 * 255 + 1];
+		char lci[2 * 255 + 1];
+		char civic[2 * 255 + 1];
+		char ssid[SSID_MAX_LEN * 2 + 1];
+
+		ssid[0] = '\0';
+		wpa_snprintf_hex(ssid, sizeof(ssid), nr->ssid.ssid,
+				 nr->ssid.ssid_len);
+
+		nrie[0] = '\0';
+		if (nr->nr)
+			wpa_snprintf_hex(nrie, sizeof(nrie),
+					 wpabuf_head(nr->nr),
+					 wpabuf_len(nr->nr));
+
+		lci[0] = '\0';
+		if (nr->lci)
+			wpa_snprintf_hex(lci, sizeof(lci),
+					 wpabuf_head(nr->lci),
+					 wpabuf_len(nr->lci));
+
+		civic[0] = '\0';
+		if (nr->civic)
+			wpa_snprintf_hex(civic, sizeof(civic),
+					 wpabuf_head(nr->civic),
+					 wpabuf_len(nr->civic));
+
+		ret = os_snprintf(pos, end - pos, MACSTR
+				  " ssid=%s%s%s%s%s%s%s%s\n",
+				  MAC2STR(nr->bssid), ssid,
+				  nr->nr ? " nr=" : "", nrie,
+				  nr->lci ? " lci=" : "", lci,
+				  nr->civic ? " civic=" : "", civic,
+				  nr->stationary ? " stat" : "");
+		if (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
 static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
 {
 	wpabuf_free(nr->nr);
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
index 9c8f4f2..bed0a2f 100644
--- a/src/ap/neighbor_db.h
+++ b/src/ap/neighbor_db.h
@@ -13,6 +13,7 @@
 struct hostapd_neighbor_entry *
 hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
 		     const struct wpa_ssid_value *ssid);
+int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen);
 int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
 			 const struct wpa_ssid_value *ssid,
 			 const struct wpabuf *nr, const struct wpabuf *lci,
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index dc73493..9f52dee 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -291,10 +291,11 @@
 
 static void wmm_addts_req(struct hostapd_data *hapd,
 			  const struct ieee80211_mgmt *mgmt,
-			  struct wmm_tspec_element *tspec, size_t len)
+			  const struct wmm_tspec_element *tspec, size_t len)
 {
 	const u8 *end = ((const u8 *) mgmt) + len;
 	int res;
+	struct wmm_tspec_element tspec_resp;
 
 	if ((const u8 *) (tspec + 1) > end) {
 		wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
@@ -306,10 +307,11 @@
 		   mgmt->u.action.u.wmm_action.dialog_token,
 		   MAC2STR(mgmt->sa));
 
-	res = wmm_process_tspec(tspec);
+	os_memcpy(&tspec_resp, tspec, sizeof(struct wmm_tspec_element));
+	res = wmm_process_tspec(&tspec_resp);
 	wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
 
-	wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
+	wmm_send_action(hapd, mgmt->sa, &tspec_resp, WMM_ACTION_CODE_ADDTS_RESP,
 			mgmt->u.action.u.wmm_action.dialog_token, res);
 }
 
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 7b690d7..6611b0e 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -2169,7 +2169,6 @@
 			wpa_printf(MSG_DEBUG,
 				   "FT: No PMKID in message 1/4 when using FT protocol");
 			pmkid = NULL;
-			pmkid_len = 0;
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
 		} else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
@@ -2199,6 +2198,8 @@
 				    &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
 		}
 	}
+	if (!pmkid)
+		pmkid_len = 0;
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
 		       sm->ANonce, pmkid, pmkid_len, 0, 0);
@@ -3139,11 +3140,12 @@
 
 SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 {
-	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, dummy_gtk[32];
 	size_t gtk_len, kde_len;
 	struct wpa_group *gsm = sm->group;
 	u8 *wpa_ie;
 	int wpa_ie_len, secure, gtkidx, encr = 0;
+	u8 *wpa_ie_buf = NULL;
 
 	SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
 	sm->TimeoutEvt = FALSE;
@@ -3177,6 +3179,35 @@
 			wpa_ie = wpa_ie + wpa_ie[1] + 2;
 		wpa_ie_len = wpa_ie[1] + 2;
 	}
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sm->wpa_auth->conf.rsnxe_override_eapol_len) {
+		u8 *obuf = sm->wpa_auth->conf.rsnxe_override_eapol;
+		size_t olen = sm->wpa_auth->conf.rsnxe_override_eapol_len;
+		const u8 *rsnxe;
+
+		wpa_hexdump(MSG_DEBUG,
+			    "TESTING: wpa_ie before RSNXE EAPOL override",
+			    wpa_ie, wpa_ie_len);
+		wpa_ie_buf = os_malloc(wpa_ie_len + olen);
+		if (!wpa_ie_buf)
+			return;
+		os_memcpy(wpa_ie_buf, wpa_ie, wpa_ie_len);
+		wpa_ie = wpa_ie_buf;
+		rsnxe = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_RSNX);
+		if (rsnxe) {
+			u8 rsnxe_len = 2 + rsnxe[1];
+
+			os_memmove((void *) rsnxe, rsnxe + rsnxe_len,
+				   wpa_ie_len - (rsnxe - wpa_ie) - rsnxe_len);
+			wpa_ie_len -= rsnxe_len;
+		}
+		os_memcpy(wpa_ie + wpa_ie_len, obuf, olen);
+		wpa_ie_len += olen;
+		wpa_hexdump(MSG_DEBUG,
+			    "TESTING: wpa_ie after RSNXE EAPOL override",
+			    wpa_ie, wpa_ie_len);
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 			"sending 3/4 msg of 4-Way Handshake");
 	if (sm->wpa == WPA_VERSION_WPA2) {
@@ -3191,7 +3222,7 @@
 			 * of GTK in the BSS.
 			 */
 			if (random_get_bytes(dummy_gtk, gtk_len) < 0)
-				return;
+				goto done;
 			gtk = dummy_gtk;
 		}
 		gtkidx = gsm->GN;
@@ -3234,7 +3265,7 @@
 #endif /* CONFIG_P2P */
 	kde = os_malloc(kde_len);
 	if (kde == NULL)
-		return;
+		goto done;
 
 	pos = kde;
 	os_memcpy(pos, wpa_ie, wpa_ie_len);
@@ -3249,8 +3280,7 @@
 		if (res < 0) {
 			wpa_printf(MSG_ERROR, "FT: Failed to insert "
 				   "PMKR1Name into RSN IE in EAPOL-Key data");
-			os_free(kde);
-			return;
+			goto done;
 		}
 		pos -= wpa_ie_len;
 		pos += elen;
@@ -3264,10 +3294,8 @@
 				  gtk, gtk_len);
 	}
 	pos = ieee80211w_kde_add(sm, pos);
-	if (ocv_oci_add(sm, &pos) < 0) {
-		os_free(kde);
-		return;
-	}
+	if (ocv_oci_add(sm, &pos) < 0)
+		goto done;
 
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -3293,8 +3321,7 @@
 		if (res < 0) {
 			wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
 				   "into EAPOL-Key Key Data");
-			os_free(kde);
-			return;
+			goto done;
 		}
 		pos += res;
 
@@ -3331,7 +3358,9 @@
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
 		       WPA_KEY_INFO_KEY_TYPE,
 		       _rsc, sm->ANonce, kde, pos - kde, 0, encr);
+done:
 	os_free(kde);
+	os_free(wpa_ie_buf);
 }
 
 
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index f627838..933a4b8 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -219,6 +219,8 @@
 	double corrupt_gtk_rekey_mic_probability;
 	u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
 	size_t own_ie_override_len;
+	u8 rsnxe_override_eapol[MAX_OWN_IE_OVERRIDE];
+	size_t rsnxe_override_eapol_len;
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_P2P
 	u8 ip_addr_go[4];
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index ddab950..7fb0923 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -118,6 +118,14 @@
 			  wpabuf_head(conf->own_ie_override),
 			  wconf->own_ie_override_len);
 	}
+	if (conf->rsnxe_override_eapol &&
+	    wpabuf_len(conf->rsnxe_override_eapol) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->rsnxe_override_eapol_len =
+			wpabuf_len(conf->rsnxe_override_eapol);
+		os_memcpy(wconf->rsnxe_override_eapol,
+			  wpabuf_head(conf->rsnxe_override_eapol),
+			  wconf->rsnxe_override_eapol_len);
+	}
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_P2P
 	os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 33caeaf..175b9fc 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1417,6 +1417,7 @@
 		data->count++;
 		wps_registrar_wps_cancel(hapd->wps->registrar);
 		ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_CANCEL);
 	}
 
 	return 0;
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index fb0cf43..7694c96 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -341,71 +341,71 @@
 	struct wpabuf *buf = NULL;
 	struct crypto_bignum *mask = NULL;
 	const u8 pwe_19_x[32] = {
-		0x19, 0xd3, 0x37, 0xc9, 0x30, 0x79, 0x2b, 0x47,
-		0x2b, 0x14, 0x5f, 0xc1, 0x5b, 0x98, 0x64, 0x0a,
-		0x0e, 0x7d, 0x3b, 0xb0, 0x7d, 0xc0, 0xad, 0xee,
-		0x6f, 0xc9, 0xdf, 0x75, 0xde, 0xc2, 0xd6, 0x94
+		0xc9, 0x30, 0x49, 0xb9, 0xe6, 0x40, 0x00, 0xf8,
+		0x48, 0x20, 0x16, 0x49, 0xe9, 0x99, 0xf2, 0xb5,
+		0xc2, 0x2d, 0xea, 0x69, 0xb5, 0x63, 0x2c, 0x9d,
+		0xf4, 0xd6, 0x33, 0xb8, 0xaa, 0x1f, 0x6c, 0x1e
 	};
 	const u8 pwe_19_y[32] = {
-		0xb7, 0x8a, 0x02, 0x39, 0x20, 0x29, 0xe7, 0xf4,
-		0x52, 0x41, 0x3d, 0x35, 0x8c, 0x88, 0xd9, 0x16,
-		0xc8, 0x90, 0xba, 0x40, 0xd9, 0x93, 0xe3, 0x2d,
-		0xd0, 0x0f, 0xfb, 0x58, 0xee, 0x62, 0x74, 0x98
+		0x73, 0x63, 0x4e, 0x94, 0xb5, 0x3d, 0x82, 0xe7,
+		0x38, 0x3a, 0x8d, 0x25, 0x81, 0x99, 0xd9, 0xdc,
+		0x1a, 0x5e, 0xe8, 0x26, 0x9d, 0x06, 0x03, 0x82,
+		0xcc, 0xbf, 0x33, 0xe6, 0x14, 0xff, 0x59, 0xa0
 	};
 	const u8 pwe_15[384] = {
-		0x59, 0x00, 0x9a, 0x32, 0xbb, 0x37, 0x84, 0x60,
-		0x27, 0xeb, 0x70, 0x20, 0x57, 0x34, 0xf1, 0xb4,
-		0xde, 0x1b, 0x48, 0xfc, 0x0e, 0xa5, 0xb8, 0x65,
-		0xb1, 0xa0, 0xd4, 0xb9, 0x42, 0x1d, 0x6d, 0xdf,
-		0x8b, 0x86, 0xeb, 0x4a, 0x2c, 0x2e, 0x38, 0x06,
-		0x52, 0xae, 0x67, 0x39, 0xed, 0x7d, 0x0c, 0xd0,
-		0xea, 0x30, 0x6e, 0x50, 0xe7, 0xb1, 0x8d, 0x91,
-		0xf0, 0x05, 0x1f, 0x16, 0xf5, 0x45, 0xa7, 0x37,
-		0x21, 0x0d, 0x8a, 0x69, 0x9a, 0xd1, 0xf1, 0x8c,
-		0x2b, 0xbb, 0xd8, 0x21, 0xa2, 0x8f, 0xcc, 0xd1,
-		0x35, 0x98, 0x66, 0xf3, 0x3c, 0x03, 0xba, 0x70,
-		0x72, 0x4e, 0xe4, 0x23, 0xb5, 0x2e, 0x96, 0x5f,
-		0xdd, 0xd1, 0xae, 0x71, 0xb1, 0xc1, 0x4b, 0x69,
-		0x4e, 0x60, 0x0a, 0x08, 0x02, 0xa1, 0x6e, 0x80,
-		0x68, 0x0a, 0xe7, 0x97, 0x9f, 0x5b, 0xbf, 0xa8,
-		0x77, 0xda, 0x5b, 0x26, 0x13, 0x0a, 0xab, 0x92,
-		0x79, 0x87, 0xa3, 0x85, 0x78, 0x74, 0xae, 0xae,
-		0x01, 0xf0, 0x31, 0x8a, 0xc3, 0x96, 0xce, 0xaa,
-		0x57, 0xbf, 0xb3, 0x57, 0xce, 0x2d, 0x2d, 0x36,
-		0xda, 0x02, 0x5b, 0x12, 0xeb, 0xff, 0x13, 0x00,
-		0x9e, 0xf7, 0xae, 0xe0, 0x47, 0xa4, 0x5d, 0x0a,
-		0x88, 0x65, 0xbc, 0x66, 0x23, 0x3e, 0xf2, 0xf1,
-		0xa0, 0x64, 0x5c, 0x6b, 0xdc, 0x81, 0xe9, 0x3c,
-		0x46, 0x4f, 0x83, 0xcf, 0x9f, 0x55, 0x33, 0x8f,
-		0xaa, 0x60, 0x4b, 0xd7, 0x21, 0x73, 0x6b, 0xdb,
-		0x26, 0xad, 0x2f, 0xb7, 0xe2, 0x42, 0x56, 0x33,
-		0xdb, 0xd6, 0xb2, 0x3a, 0x7d, 0x75, 0x87, 0xda,
-		0x86, 0xc4, 0xe9, 0x41, 0x8d, 0x63, 0x19, 0x8e,
-		0x8b, 0x17, 0x95, 0xfe, 0x2b, 0x96, 0xa0, 0x38,
-		0xf1, 0xe2, 0x1d, 0x42, 0xa9, 0xe3, 0x8a, 0xa1,
-		0x61, 0x62, 0x10, 0xf8, 0xb3, 0xb2, 0x2c, 0x7b,
-		0xdf, 0xba, 0x74, 0xb2, 0x5b, 0xf6, 0xa9, 0xae,
-		0x1d, 0x21, 0x0d, 0xc0, 0x48, 0x20, 0xfc, 0x28,
-		0xf6, 0x22, 0xd2, 0xf6, 0x9c, 0x71, 0x3f, 0x9f,
-		0x32, 0xd6, 0xbb, 0x9b, 0xd3, 0x87, 0x25, 0xcf,
-		0x62, 0xd1, 0x68, 0xba, 0x55, 0x3b, 0x74, 0x2b,
-		0x1d, 0x5a, 0xe4, 0x94, 0x59, 0x3b, 0x13, 0x21,
-		0x15, 0x87, 0x3b, 0x09, 0x0e, 0xcf, 0x35, 0x60,
-		0x04, 0xa8, 0xde, 0xa1, 0x09, 0xca, 0xb8, 0x35,
-		0x1e, 0x16, 0x61, 0xed, 0xa1, 0x1f, 0x8c, 0x92,
-		0x83, 0xa5, 0x27, 0x92, 0xf2, 0x80, 0xc3, 0xcb,
-		0xdd, 0x3c, 0x0c, 0xf5, 0x8d, 0x69, 0xb3, 0xe4,
-		0xd5, 0x49, 0x4d, 0x62, 0xcb, 0xb8, 0xe3, 0x9f,
-		0x89, 0xb5, 0x57, 0xff, 0xef, 0x12, 0x37, 0x05,
-		0xb6, 0x35, 0xe5, 0xc6, 0xd9, 0x23, 0xe2, 0xeb,
-		0xe4, 0x0d, 0x1a, 0x30, 0x8f, 0x73, 0x70, 0x3a,
-		0xef, 0x5a, 0xd1, 0x8c, 0x18, 0x34, 0x1e, 0xf0,
-		0xb9, 0x08, 0x57, 0xab, 0xcb, 0x5c, 0x87, 0x10
+		0x69, 0x68, 0x73, 0x65, 0x8f, 0x65, 0x31, 0x42,
+		0x9f, 0x97, 0x39, 0x6f, 0xb8, 0x5f, 0x89, 0xe1,
+		0xfc, 0xd2, 0xf6, 0x92, 0x19, 0xa9, 0x0e, 0x82,
+		0x2f, 0xf7, 0xf4, 0xbc, 0x0b, 0xd8, 0xa7, 0x9f,
+		0xf0, 0x80, 0x35, 0x31, 0x6f, 0xca, 0xe1, 0xa5,
+		0x39, 0x77, 0xdc, 0x11, 0x2b, 0x0b, 0xfe, 0x2e,
+		0x6f, 0x65, 0x6d, 0xc7, 0xd4, 0xa4, 0x5b, 0x08,
+		0x1f, 0xd9, 0xbb, 0xe2, 0x22, 0x85, 0x31, 0x81,
+		0x79, 0x70, 0xbe, 0xa1, 0x66, 0x58, 0x4a, 0x09,
+		0x3c, 0x57, 0x34, 0x3c, 0x9d, 0x57, 0x8f, 0x42,
+		0x58, 0xd0, 0x39, 0x81, 0xdb, 0x8f, 0x79, 0xa2,
+		0x1b, 0x01, 0xcd, 0x27, 0xc9, 0xae, 0xcf, 0xcb,
+		0x9c, 0xdb, 0x1f, 0x84, 0xb8, 0x88, 0x4e, 0x8f,
+		0x50, 0x66, 0xb4, 0x29, 0x83, 0x1e, 0xb9, 0x89,
+		0x0c, 0xa5, 0x47, 0x21, 0xba, 0x10, 0xd5, 0xaa,
+		0x1a, 0x80, 0xce, 0xf1, 0x4c, 0xad, 0x16, 0xda,
+		0x57, 0xb2, 0x41, 0x8a, 0xbe, 0x4b, 0x8c, 0xb0,
+		0xb2, 0xeb, 0xf7, 0xa8, 0x0e, 0x3e, 0xcf, 0x22,
+		0x8f, 0xd8, 0xb6, 0xdb, 0x79, 0x9c, 0x9b, 0x80,
+		0xaf, 0xd7, 0x14, 0xad, 0x51, 0x82, 0xf4, 0x64,
+		0xb6, 0x3f, 0x4c, 0x6c, 0xe5, 0x3f, 0xaa, 0x6f,
+		0xbf, 0x3d, 0xc2, 0x3f, 0x77, 0xfd, 0xcb, 0xe1,
+		0x9c, 0xe3, 0x1e, 0x8a, 0x0e, 0x97, 0xe2, 0x2b,
+		0xe2, 0xdd, 0x37, 0x39, 0x88, 0xc2, 0x8e, 0xbe,
+		0xfa, 0xac, 0x3d, 0x5b, 0x62, 0x2e, 0x1e, 0x74,
+		0xa0, 0x9a, 0xf8, 0xed, 0xfa, 0xe1, 0xce, 0x9c,
+		0xab, 0xbb, 0xdc, 0x36, 0xb1, 0x28, 0x46, 0x3c,
+		0x7e, 0xa8, 0xbd, 0xb9, 0x36, 0x4c, 0x26, 0x75,
+		0xe0, 0x17, 0x73, 0x1f, 0xe0, 0xfe, 0xf6, 0x49,
+		0xfa, 0xa0, 0x45, 0xf4, 0x44, 0x05, 0x20, 0x27,
+		0x25, 0xc2, 0x99, 0xde, 0x27, 0x8b, 0x70, 0xdc,
+		0x54, 0x60, 0x90, 0x02, 0x1e, 0x29, 0x97, 0x9a,
+		0xc4, 0xe7, 0xb6, 0xf5, 0x8b, 0xae, 0x7c, 0x34,
+		0xaa, 0xef, 0x9b, 0xc6, 0x30, 0xf2, 0x80, 0x8d,
+		0x80, 0x78, 0xc2, 0x55, 0x63, 0xa0, 0xa1, 0x38,
+		0x70, 0xfb, 0xf4, 0x74, 0x8d, 0xcd, 0x87, 0x90,
+		0xb4, 0x54, 0xc3, 0x75, 0xdf, 0x10, 0xc5, 0xb6,
+		0xb2, 0x08, 0x59, 0x61, 0xe6, 0x68, 0xa5, 0x82,
+		0xf8, 0x8f, 0x47, 0x30, 0x43, 0xb4, 0xdc, 0x31,
+		0xfc, 0xbc, 0x69, 0xe7, 0xb4, 0x94, 0xb0, 0x6a,
+		0x60, 0x59, 0x80, 0x2e, 0xd3, 0xa4, 0xe8, 0x97,
+		0xa2, 0xa3, 0xc9, 0x08, 0x4b, 0x27, 0x6c, 0xc1,
+		0x37, 0xe8, 0xfc, 0x5c, 0xe2, 0x54, 0x30, 0x3e,
+		0xf8, 0xfe, 0xa2, 0xfc, 0xbb, 0xbd, 0x88, 0x6c,
+		0x92, 0xa3, 0x2a, 0x40, 0x7a, 0x2c, 0x22, 0x38,
+		0x8c, 0x86, 0x86, 0xfe, 0xb9, 0xd4, 0x6b, 0xd6,
+		0x47, 0x88, 0xa7, 0xf6, 0x8e, 0x0f, 0x14, 0xad,
+		0x1e, 0xac, 0xcf, 0x33, 0x01, 0x99, 0xc1, 0x62
 	};
 	int pt_groups[] = { 19, 20, 21, 25, 26, 28, 29, 30, 15, 0 };
 	struct sae_pt *pt_info, *pt;
-	u8 addr1b[ETH_ALEN] = { 0x3b, 0x36, 0xc2, 0x8b, 0x83, 0x03 };
-	u8 addr2b[ETH_ALEN] = { 0x58, 0x36, 0xc0, 0x64, 0x2d, 0x31 };
+	const u8 addr1b[ETH_ALEN] = { 0x00, 0x09, 0x5b, 0x66, 0xec, 0x1e };
+	const u8 addr2b[ETH_ALEN] = { 0x00, 0x0b, 0x6b, 0xd9, 0x02, 0x46 };
 
 	os_memset(&sae, 0, sizeof(sae));
 	buf = wpabuf_alloc(1000);
diff --git a/src/common/defs.h b/src/common/defs.h
index 4faf1c8..e2fa4b2 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -416,6 +416,10 @@
 	CHAN_WIDTH_80,
 	CHAN_WIDTH_80P80,
 	CHAN_WIDTH_160,
+	CHAN_WIDTH_2160,
+	CHAN_WIDTH_4320,
+	CHAN_WIDTH_6480,
+	CHAN_WIDTH_8640,
 	CHAN_WIDTH_UNKNOWN
 };
 
diff --git a/src/common/dpp.c b/src/common/dpp.c
index 44cb910..7542c66 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -33,6 +33,8 @@
 #include "dpp.h"
 
 
+static const char * dpp_netrole_str(enum dpp_netrole netrole);
+
 #ifdef CONFIG_TESTING_OPTIONS
 enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
 u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
@@ -830,6 +832,8 @@
 		return "QRCODE";
 	case DPP_BOOTSTRAP_PKEX:
 		return "PKEX";
+	case DPP_BOOTSTRAP_NFC_URI:
+		return "NFC-URI";
 	}
 	return "??";
 }
@@ -1007,8 +1011,7 @@
 	if (!end)
 		return -1;
 
-	data = base64_decode((const unsigned char *) info, end - info,
-			     &data_len);
+	data = base64_decode(info, end - info, &data_len);
 	if (!data) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Invalid base64 encoding on URI public-key");
@@ -1182,17 +1185,6 @@
 }
 
 
-struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri)
-{
-	struct dpp_bootstrap_info *bi;
-
-	bi = dpp_parse_uri(uri);
-	if (bi)
-		bi->type = DPP_BOOTSTRAP_QR_CODE;
-	return bi;
-}
-
-
 static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
 {
 	EC_KEY *eckey;
@@ -1482,7 +1474,7 @@
 char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
 		  const u8 *privkey, size_t privkey_len)
 {
-	unsigned char *base64 = NULL;
+	char *base64 = NULL;
 	char *pos, *end;
 	size_t len;
 	struct wpabuf *der = NULL;
@@ -1528,7 +1520,7 @@
 	der = NULL;
 	if (!base64)
 		goto fail;
-	pos = (char *) base64;
+	pos = base64;
 	end = pos + len;
 	for (;;) {
 		pos = os_strchr(pos, '\n');
@@ -1536,7 +1528,7 @@
 			break;
 		os_memmove(pos, pos + 1, end - pos);
 	}
-	return (char *) base64;
+	return base64;
 fail:
 	os_free(base64);
 	wpabuf_free(der);
@@ -2541,13 +2533,13 @@
 
 
 struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
-					  const char *name, int netrole_ap,
+					  const char *name,
+					  enum dpp_netrole netrole,
 					  const char *mud_url, int *opclasses)
 {
-	size_t len, nlen;
+	size_t len, name_len;
 	const char *tech = "infra";
 	const char *dpp_name;
-	char *nbuf;
 	struct wpabuf *buf, *json;
 
 #ifdef CONFIG_TESTING_OPTIONS
@@ -2560,39 +2552,38 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	dpp_name = name ? name : "Test";
-	len = os_strlen(dpp_name);
-	nlen = len * 6 + 1;
-	nbuf = os_malloc(nlen);
-	if (!nbuf)
-		return NULL;
-	json_escape_string(nbuf, nlen, dpp_name, len);
+	name_len = os_strlen(dpp_name);
 
-	len = 100 + os_strlen(nbuf) + int_array_len(opclasses) * 4;
+	len = 100 + name_len * 6 + 1 + int_array_len(opclasses) * 4;
 	if (mud_url && mud_url[0])
 		len += 10 + os_strlen(mud_url);
 	json = wpabuf_alloc(len);
-	if (!json) {
-		os_free(nbuf);
+	if (!json)
+		return NULL;
+
+	json_start_object(json, NULL);
+	if (json_add_string_escape(json, "name", dpp_name, name_len) < 0) {
+		wpabuf_free(json);
 		return NULL;
 	}
-
-	wpabuf_printf(json,
-		      "{\"name\":\"%s\","
-		      "\"wi-fi_tech\":\"%s\","
-		      "\"netRole\":\"%s\"",
-		      nbuf, tech, netrole_ap ? "ap" : "sta");
-	if (mud_url && mud_url[0])
-		wpabuf_printf(json, ",\"mudurl\":\"%s\"", mud_url);
+	json_value_sep(json);
+	json_add_string(json, "wi-fi_tech", tech);
+	json_value_sep(json);
+	json_add_string(json, "netRole", dpp_netrole_str(netrole));
+	if (mud_url && mud_url[0]) {
+		json_value_sep(json);
+		json_add_string(json, "mudurl", mud_url);
+	}
 	if (opclasses) {
 		int i;
 
-		wpabuf_put_str(json, ",\"bandSupport\":[");
+		json_value_sep(json);
+		json_start_array(json, "bandSupport");
 		for (i = 0; opclasses[i]; i++)
 			wpabuf_printf(json, "%s%u", i ? "," : "", opclasses[i]);
-		wpabuf_put_str(json, "]");
+		json_end_array(json);
 	}
-	wpabuf_put_str(json, "}");
-	os_free(nbuf);
+	json_end_object(json);
 
 	buf = dpp_build_conf_req(auth, wpabuf_head(json));
 	wpabuf_free(json);
@@ -4435,6 +4426,16 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 	}
 
+	pos = os_strstr(cmd, " ssid_charset=");
+	if (pos) {
+		if (conf_ap) {
+			wpa_printf(MSG_INFO,
+				   "DPP: ssid64 option (ssid_charset param) not allowed for AP enrollee");
+			goto fail;
+		}
+		conf->ssid_charset = atoi(pos + 14);
+	}
+
 	pos = os_strstr(cmd, " pass=");
 	if (pos) {
 		size_t pass_len;
@@ -4637,7 +4638,6 @@
 		     struct dpp_configuration *conf, size_t tailroom)
 {
 	struct wpabuf *buf;
-	char ssid[6 * sizeof(conf->ssid) + 1];
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (auth->discovery_override)
@@ -4647,21 +4647,35 @@
 	buf = wpabuf_alloc(200 + tailroom);
 	if (!buf)
 		return NULL;
-	wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":");
+	json_start_object(buf, NULL);
+	json_add_string(buf, "wi-fi_tech", "infra");
+	json_value_sep(buf);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (auth->discovery_override) {
 		wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'",
 			   auth->discovery_override);
+		wpabuf_put_str(buf, "\"discovery\":");
 		wpabuf_put_str(buf, auth->discovery_override);
-		wpabuf_put_u8(buf, ',');
+		json_value_sep(buf);
 		return buf;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	wpabuf_put_str(buf, "{\"ssid\":\"");
-	json_escape_string(ssid, sizeof(ssid),
-			   (const char *) conf->ssid, conf->ssid_len);
-	wpabuf_put_str(buf, ssid);
-	wpabuf_put_str(buf, "\"},");
+	json_start_object(buf, "discovery");
+	if (((!conf->ssid_charset || auth->peer_version < 2) &&
+	     json_add_string_escape(buf, "ssid", conf->ssid,
+				    conf->ssid_len) < 0) ||
+	    ((conf->ssid_charset && auth->peer_version >= 2) &&
+	     json_add_base64url(buf, "ssid64", conf->ssid,
+				conf->ssid_len) < 0)) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+	if (conf->ssid_charset > 0) {
+		json_value_sep(buf);
+		json_add_int(buf, "ssid_charset", conf->ssid_charset);
+	}
+	json_end_object(buf);
+	json_value_sep(buf);
 
 	return buf;
 }
@@ -4672,37 +4686,32 @@
 {
 	struct wpabuf *pub;
 	const u8 *pos;
-	char *x = NULL, *y = NULL;
 	int ret = -1;
 
 	pub = dpp_get_pubkey_point(key, 0);
 	if (!pub)
 		goto fail;
-	pos = wpabuf_head(pub);
-	x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
-	pos += curve->prime_len;
-	y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
-	if (!x || !y)
-		goto fail;
 
-	wpabuf_put_str(buf, "\"");
-	wpabuf_put_str(buf, name);
-	wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\"");
-	wpabuf_put_str(buf, curve->jwk_crv);
-	wpabuf_put_str(buf, "\",\"x\":\"");
-	wpabuf_put_str(buf, x);
-	wpabuf_put_str(buf, "\",\"y\":\"");
-	wpabuf_put_str(buf, y);
+	json_start_object(buf, name);
+	json_add_string(buf, "kty", "EC");
+	json_value_sep(buf);
+	json_add_string(buf, "crv", curve->jwk_crv);
+	json_value_sep(buf);
+	pos = wpabuf_head(pub);
+	if (json_add_base64url(buf, "x", pos, curve->prime_len) < 0)
+		goto fail;
+	json_value_sep(buf);
+	pos += curve->prime_len;
+	if (json_add_base64url(buf, "y", pos, curve->prime_len) < 0)
+		goto fail;
 	if (kid) {
-		wpabuf_put_str(buf, "\",\"kid\":\"");
-		wpabuf_put_str(buf, kid);
+		json_value_sep(buf);
+		json_add_string(buf, "kid", kid);
 	}
-	wpabuf_put_str(buf, "\"}");
+	json_end_object(buf);
 	ret = 0;
 fail:
 	wpabuf_free(pub);
-	os_free(x);
-	os_free(y);
 	return ret;
 }
 
@@ -4711,23 +4720,15 @@
 					 struct dpp_configuration *conf)
 {
 	if (conf->passphrase && os_strlen(conf->passphrase) < 64) {
-		char pass[63 * 6 + 1];
-
-		json_escape_string(pass, sizeof(pass), conf->passphrase,
-				   os_strlen(conf->passphrase));
-		wpabuf_put_str(buf, "\"pass\":\"");
-		wpabuf_put_str(buf, pass);
-		wpabuf_put_str(buf, "\"");
-		os_memset(pass, 0, sizeof(pass));
+		json_add_string_escape(buf, "pass", conf->passphrase,
+				       os_strlen(conf->passphrase));
 	} else if (conf->psk_set) {
 		char psk[2 * sizeof(conf->psk) + 1];
 
 		wpa_snprintf_hex(psk, sizeof(psk),
 				 conf->psk, sizeof(conf->psk));
-		wpabuf_put_str(buf, "\"psk_hex\":\"");
-		wpabuf_put_str(buf, psk);
-		wpabuf_put_str(buf, "\"");
-		os_memset(psk, 0, sizeof(psk));
+		json_add_string(buf, "psk_hex", psk);
+		forced_memzero(psk, sizeof(psk));
 	}
 }
 
@@ -4739,6 +4740,8 @@
 		return "sta";
 	case DPP_NETROLE_AP:
 		return "ap";
+	case DPP_NETROLE_CONFIGURATOR:
+		return "configurator";
 	default:
 		return "??";
 	}
@@ -4753,7 +4756,7 @@
 	char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
 	size_t tailroom;
 	const struct dpp_curve_params *curve;
-	char jws_prot_hdr[100];
+	struct wpabuf *jws_prot_hdr;
 	size_t signed1_len, signed2_len, signed3_len;
 	struct wpabuf *dppcon = NULL;
 	unsigned char *signature = NULL;
@@ -4814,15 +4817,21 @@
 				   auth->groups_override);
 			wpabuf_put_str(dppcon, "\"groups\":");
 			wpabuf_put_str(dppcon, auth->groups_override);
-			wpabuf_put_u8(dppcon, ',');
+			json_value_sep(dppcon);
 		}
 		goto skip_groups;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",",
-		      conf->group_id ? conf->group_id : "*");
-	wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],",
-		      dpp_netrole_str(conf->netrole));
+	json_start_object(dppcon, NULL);
+	json_start_array(dppcon, "groups");
+	json_start_object(dppcon, NULL);
+	json_add_string(dppcon, "groupId",
+			conf->group_id ? conf->group_id : "*");
+	json_value_sep(dppcon);
+	json_add_string(dppcon, "netRole", dpp_netrole_str(conf->netrole));
+	json_end_object(dppcon);
+	json_end_array(dppcon);
+	json_value_sep(dppcon);
 #ifdef CONFIG_TESTING_OPTIONS
 skip_groups:
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -4833,30 +4842,40 @@
 	}
 	if (conf->netaccesskey_expiry) {
 		struct os_tm tm;
+		char expiry[30];
 
 		if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) {
 			wpa_printf(MSG_DEBUG,
 				   "DPP: Failed to generate expiry string");
 			goto fail;
 		}
-		wpabuf_printf(dppcon,
-			      ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"",
-			      tm.year, tm.month, tm.day,
-			      tm.hour, tm.min, tm.sec);
+		os_snprintf(expiry, sizeof(expiry),
+			    "%04u-%02u-%02uT%02u:%02u:%02uZ",
+			    tm.year, tm.month, tm.day,
+			    tm.hour, tm.min, tm.sec);
+		json_value_sep(dppcon);
+		json_add_string(dppcon, "expiry", expiry);
 	}
-	wpabuf_put_u8(dppcon, '}');
+	json_end_object(dppcon);
 	wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
 		   (const char *) wpabuf_head(dppcon));
 
-	os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr),
-		    "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}",
-		    auth->conf->kid, curve->jws_alg);
-	signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr,
-					     os_strlen(jws_prot_hdr),
-					     &signed1_len, 0);
-	signed2 = (char *) base64_url_encode(wpabuf_head(dppcon),
-					     wpabuf_len(dppcon),
-					     &signed2_len, 0);
+	jws_prot_hdr = wpabuf_alloc(100);
+	if (!jws_prot_hdr)
+		goto fail;
+	json_start_object(jws_prot_hdr, NULL);
+	json_add_string(jws_prot_hdr, "typ", "dppCon");
+	json_value_sep(jws_prot_hdr);
+	json_add_string(jws_prot_hdr, "kid", auth->conf->kid);
+	json_value_sep(jws_prot_hdr);
+	json_add_string(jws_prot_hdr, "alg", curve->jws_alg);
+	json_end_object(jws_prot_hdr);
+	signed1 = base64_url_encode(wpabuf_head(jws_prot_hdr),
+				    wpabuf_len(jws_prot_hdr),
+				    &signed1_len);
+	wpabuf_free(jws_prot_hdr);
+	signed2 = base64_url_encode(wpabuf_head(dppcon), wpabuf_len(dppcon),
+				    &signed2_len);
 	if (!signed1 || !signed2)
 		goto fail;
 
@@ -4906,8 +4925,7 @@
 	signature_len = 2 * curve->prime_len;
 	wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
 		    signature, signature_len);
-	signed3 = (char *) base64_url_encode(signature, signature_len,
-					     &signed3_len, 0);
+	signed3 = base64_url_encode(signature, signature_len, &signed3_len);
 	if (!signed3)
 		goto fail;
 
@@ -4925,10 +4943,12 @@
 		akm_str = dpp_akm_selector_str(akm);
 	else
 		akm_str = dpp_akm_str(akm);
-	wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", akm_str);
+	json_start_object(buf, "cred");
+	json_add_string(buf, "akm", akm_str);
+	json_value_sep(buf);
 	if (incl_legacy) {
 		dpp_build_legacy_cred_params(buf, conf);
-		wpabuf_put_str(buf, ",");
+		json_value_sep(buf);
 	}
 	wpabuf_put_str(buf, "\"signedConnector\":\"");
 	wpabuf_put_str(buf, signed1);
@@ -4936,14 +4956,16 @@
 	wpabuf_put_str(buf, signed2);
 	wpabuf_put_u8(buf, '.');
 	wpabuf_put_str(buf, signed3);
-	wpabuf_put_str(buf, "\",");
+	wpabuf_put_str(buf, "\"");
+	json_value_sep(buf);
 	if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
 			  curve) < 0) {
 		wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
 		goto fail;
 	}
 
-	wpabuf_put_str(buf, "}}");
+	json_end_object(buf);
+	json_end_object(buf);
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
 			      wpabuf_head(buf), wpabuf_len(buf));
@@ -4980,9 +5002,12 @@
 		akm_str = dpp_akm_selector_str(conf->akm);
 	else
 		akm_str = dpp_akm_str(conf->akm);
-	wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", akm_str);
+	json_start_object(buf, "cred");
+	json_add_string(buf, "akm", akm_str);
+	json_value_sep(buf);
 	dpp_build_legacy_cred_params(buf, conf);
-	wpabuf_put_str(buf, "}}");
+	json_end_object(buf);
+	json_end_object(buf);
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
 			      wpabuf_head(buf), wpabuf_len(buf));
@@ -4992,9 +5017,10 @@
 
 
 static struct wpabuf *
-dpp_build_conf_obj(struct dpp_authentication *auth, int ap, int idx)
+dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole,
+		   int idx)
 {
-	struct dpp_configuration *conf;
+	struct dpp_configuration *conf = NULL;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (auth->config_obj_override) {
@@ -5006,17 +5032,22 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-	if (idx == 0)
-		conf = ap ? auth->conf_ap : auth->conf_sta;
-	else if (idx == 1)
-		conf = ap ? auth->conf2_ap : auth->conf2_sta;
-	else
-		conf = NULL;
+	if (idx == 0) {
+		if (netrole == DPP_NETROLE_STA)
+			conf = auth->conf_sta;
+		else if (netrole == DPP_NETROLE_AP)
+			conf = auth->conf_ap;
+	} else if (idx == 1) {
+		if (netrole == DPP_NETROLE_STA)
+			conf = auth->conf2_sta;
+		else if (netrole == DPP_NETROLE_AP)
+			conf = auth->conf2_ap;
+	}
 	if (!conf) {
 		if (idx == 0)
 			wpa_printf(MSG_DEBUG,
 				   "DPP: No configuration available for Enrollee(%s) - reject configuration request",
-				   ap ? "ap" : "sta");
+				   dpp_netrole_str(netrole));
 		return NULL;
 	}
 
@@ -5028,7 +5059,7 @@
 
 static struct wpabuf *
 dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
-		    u16 e_nonce_len, int ap)
+		    u16 e_nonce_len, enum dpp_netrole netrole)
 {
 	struct wpabuf *conf, *conf2 = NULL;
 	size_t clear_len, attr_len;
@@ -5038,11 +5069,11 @@
 	size_t len[1];
 	enum dpp_status_error status;
 
-	conf = dpp_build_conf_obj(auth, ap, 0);
+	conf = dpp_build_conf_obj(auth, netrole, 0);
 	if (conf) {
 		wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
 				  wpabuf_head(conf), wpabuf_len(conf));
-		conf2 = dpp_build_conf_obj(auth, ap, 1);
+		conf2 = dpp_build_conf_obj(auth, netrole, 1);
 	}
 	status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
 	auth->conf_resp_status = status;
@@ -5053,7 +5084,8 @@
 		clear_len += 4 + wpabuf_len(conf);
 	if (conf2)
 		clear_len += 4 + wpabuf_len(conf2);
-	if (auth->peer_version >= 2 && auth->send_conn_status && !ap)
+	if (auth->peer_version >= 2 && auth->send_conn_status &&
+	    netrole == DPP_NETROLE_STA)
 		clear_len += 4;
 	clear = wpabuf_alloc(clear_len);
 	attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
@@ -5111,7 +5143,8 @@
 			   "DPP: Second Config Object available, but peer does not support more than one");
 	}
 
-	if (auth->peer_version >= 2 && auth->send_conn_status && !ap) {
+	if (auth->peer_version >= 2 && auth->send_conn_status &&
+	    netrole == DPP_NETROLE_STA) {
 		wpa_printf(MSG_DEBUG, "DPP: sendConnStatus");
 		wpabuf_put_le16(clear, DPP_ATTR_SEND_CONN_STATUS);
 		wpabuf_put_le16(clear, 0);
@@ -5185,7 +5218,7 @@
 	size_t unwrapped_len = 0;
 	struct wpabuf *resp = NULL;
 	struct json_token *root = NULL, *token;
-	int ap;
+	enum dpp_netrole netrole;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
@@ -5283,9 +5316,11 @@
 	}
 	wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
 	if (os_strcmp(token->string, "sta") == 0) {
-		ap = 0;
+		netrole = DPP_NETROLE_STA;
 	} else if (os_strcmp(token->string, "ap") == 0) {
-		ap = 1;
+		netrole = DPP_NETROLE_AP;
+	} else if (os_strcmp(token->string, "configurator") == 0) {
+		netrole = DPP_NETROLE_CONFIGURATOR;
 	} else {
 		wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
 			   token->string);
@@ -5319,7 +5354,7 @@
 		}
 	}
 
-	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap);
+	resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole);
 
 fail:
 	json_free(root);
@@ -5825,8 +5860,7 @@
 		ret = DPP_STATUS_INVALID_CONNECTOR;
 		goto fail;
 	}
-	prot_hdr = base64_url_decode((const unsigned char *) pos,
-				     end - pos, &prot_hdr_len);
+	prot_hdr = base64_url_decode(pos, end - pos, &prot_hdr_len);
 	if (!prot_hdr) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Failed to base64url decode signedConnector JWS Protected Header");
@@ -5858,8 +5892,7 @@
 		goto fail;
 	}
 	signed_end = end - 1;
-	info->payload = base64_url_decode((const unsigned char *) pos,
-					  end - pos, &info->payload_len);
+	info->payload = base64_url_decode(pos, end - pos, &info->payload_len);
 	if (!info->payload) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Failed to base64url decode signedConnector JWS Payload");
@@ -5870,8 +5903,7 @@
 			  "DPP: signedConnector - JWS Payload",
 			  info->payload, info->payload_len);
 	pos = end + 1;
-	signature = base64_url_decode((const unsigned char *) pos,
-				      os_strlen(pos), &signature_len);
+	signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
 	if (!signature) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Failed to base64url decode signedConnector signature");
@@ -6126,6 +6158,7 @@
 	int ret = -1;
 	struct json_token *root, *token, *discovery, *cred;
 	struct dpp_config_obj *conf;
+	struct wpabuf *ssid64 = NULL;
 
 	root = json_parse((const char *) conf_obj, conf_obj_len);
 	if (!root)
@@ -6153,28 +6186,52 @@
 		goto fail;
 	}
 
-	token = json_get_member(discovery, "ssid");
-	if (!token || token->type != JSON_STRING) {
-		dpp_auth_fail(auth, "No discovery::ssid string value found");
-		goto fail;
-	}
-	wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
-			  token->string, os_strlen(token->string));
-	if (os_strlen(token->string) > SSID_MAX_LEN) {
-		dpp_auth_fail(auth, "Too long discovery::ssid string value");
-		goto fail;
+	ssid64 = json_get_member_base64url(discovery, "ssid64");
+	if (ssid64) {
+		wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid64",
+				  wpabuf_head(ssid64), wpabuf_len(ssid64));
+		if (wpabuf_len(ssid64) > SSID_MAX_LEN) {
+			dpp_auth_fail(auth, "Too long discovery::ssid64 value");
+			goto fail;
+		}
+	} else {
+		token = json_get_member(discovery, "ssid");
+		if (!token || token->type != JSON_STRING) {
+			dpp_auth_fail(auth,
+				      "No discovery::ssid string value found");
+			goto fail;
+		}
+		wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
+				  token->string, os_strlen(token->string));
+		if (os_strlen(token->string) > SSID_MAX_LEN) {
+			dpp_auth_fail(auth,
+				      "Too long discovery::ssid string value");
+			goto fail;
+		}
 	}
 
 	if (auth->num_conf_obj == DPP_MAX_CONF_OBJ) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No room for this many Config Objects - ignore this one");
-		json_free(root);
-		return 0;
+		ret = 0;
+		goto fail;
 	}
 	conf = &auth->conf_obj[auth->num_conf_obj++];
 
-	conf->ssid_len = os_strlen(token->string);
-	os_memcpy(conf->ssid, token->string, conf->ssid_len);
+	if (ssid64) {
+		conf->ssid_len = wpabuf_len(ssid64);
+		os_memcpy(conf->ssid, wpabuf_head(ssid64), conf->ssid_len);
+	} else {
+		conf->ssid_len = os_strlen(token->string);
+		os_memcpy(conf->ssid, token->string, conf->ssid_len);
+	}
+
+	token = json_get_member(discovery, "ssid_charset");
+	if (token && token->type == JSON_NUMBER) {
+		conf->ssid_charset = token->number;
+		wpa_printf(MSG_DEBUG, "DPP: ssid_charset=%d",
+			   conf->ssid_charset);
+	}
 
 	cred = json_get_member(root, "cred");
 	if (!cred || cred->type != JSON_OBJECT) {
@@ -6205,6 +6262,7 @@
 	wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully");
 	ret = 0;
 fail:
+	wpabuf_free(ssid64);
 	json_free(root);
 	return ret;
 }
@@ -6493,6 +6551,7 @@
 	size_t unwrapped_len = 0;
 	enum dpp_status_error ret = 256;
 	struct json_token *root = NULL, *token;
+	struct wpabuf *ssid64;
 
 	*ssid_len = 0;
 	*channel_list = NULL;
@@ -6567,12 +6626,12 @@
 		goto fail;
 	}
 
-	token = json_get_member(root, "ssid");
-	if (token && token->type == JSON_STRING &&
-	    os_strlen(token->string) <= SSID_MAX_LEN) {
-		*ssid_len = os_strlen(token->string);
-		os_memcpy(ssid, token->string, *ssid_len);
+	ssid64 = json_get_member_base64url(root, "ssid64");
+	if (ssid64 && wpabuf_len(ssid64) <= SSID_MAX_LEN) {
+		*ssid_len = wpabuf_len(ssid64);
+		os_memcpy(ssid, wpabuf_head(ssid64), *ssid_len);
 	}
+	wpabuf_free(ssid64);
 
 	token = json_get_member(root, "channelList");
 	if (token && token->type == JSON_STRING &&
@@ -6599,7 +6658,7 @@
 					     const u8 *ssid, size_t ssid_len,
 					     const char *channel_list)
 {
-	struct wpabuf *msg, *clear, *json;
+	struct wpabuf *msg = NULL, *clear = NULL, *json;
 	size_t nonce_len, clear_len, attr_len;
 	const u8 *addr[2];
 	size_t len[2];
@@ -6608,19 +6667,18 @@
 	json = wpabuf_alloc(1000);
 	if (!json)
 		return NULL;
-	wpabuf_printf(json, "{\"result\":%d", result);
+	json_start_object(json, NULL);
+	json_add_int(json, "result", result);
 	if (ssid) {
-		char ssid_str[6 * SSID_MAX_LEN + 1];
-
-		wpabuf_put_str(json, ",\"ssid\":\"");
-		json_escape_string(ssid_str, sizeof(ssid_str),
-				   (const char *) ssid, ssid_len);
-		wpabuf_put_str(json, ssid_str);
-		wpabuf_put_str(json, "\"");
+		json_value_sep(json);
+		if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0)
+			goto fail;
 	}
-	if (channel_list)
-		wpabuf_printf(json, ",\"channelList\":\"%s\"", channel_list);
-	wpabuf_put_str(json, "}");
+	if (channel_list) {
+		json_value_sep(json);
+		json_add_string(json, "channelList", channel_list);
+	}
+	json_end_object(json);
 	wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
 			  wpabuf_head(json), wpabuf_len(json));
 
@@ -6761,8 +6819,7 @@
 		goto fail;
 	}
 
-	conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash),
-					       NULL, 0);
+	conf->kid = base64_url_encode(kid_hash, sizeof(kid_hash), NULL);
 	if (!conf->kid)
 		goto fail;
 out:
@@ -6808,8 +6865,11 @@
 	dpp_copy_csign(&auth->conf_obj[0], auth->conf->csign);
 
 	conf_obj = dpp_build_conf_obj(auth, ap, 0);
-	if (!conf_obj)
+	if (!conf_obj) {
+		wpabuf_free(auth->conf_obj[0].c_sign_key);
+		auth->conf_obj[0].c_sign_key = NULL;
 		goto fail;
+	}
 	ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
 				 wpabuf_len(conf_obj));
 fail:
@@ -7024,8 +7084,7 @@
 		wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
 		goto fail;
 	}
-	own_conn = base64_url_decode((const unsigned char *) pos,
-				     end - pos, &own_conn_len);
+	own_conn = base64_url_decode(pos, end - pos, &own_conn_len);
 	if (!own_conn) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Failed to base64url decode own signedConnector JWS Payload");
@@ -8759,8 +8818,7 @@
 
 	wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
 		   pos);
-	signature = base64_url_decode((const unsigned char *) pos,
-				      os_strlen(pos), &signature_len);
+	signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
 	if (!signature || signature_len == 0)
 		goto fail;
 	wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
@@ -8768,8 +8826,7 @@
 	signature[signature_len - 1] ^= 0x01;
 	wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
 		    signature, signature_len);
-	signed3 = (char *) base64_url_encode(signature, signature_len,
-					     &signed3_len, 0);
+	signed3 = base64_url_encode(signature, signature_len, &signed3_len);
 	if (!signed3)
 		goto fail;
 	os_memcpy(pos, signed3, signed3_len);
@@ -8917,10 +8974,30 @@
 	if (!dpp)
 		return NULL;
 
-	bi = dpp_parse_qr_code(uri);
+	bi = dpp_parse_uri(uri);
 	if (!bi)
 		return NULL;
 
+	bi->type = DPP_BOOTSTRAP_QR_CODE;
+	bi->id = dpp_next_id(dpp);
+	dl_list_add(&dpp->bootstrap, &bi->list);
+	return bi;
+}
+
+
+struct dpp_bootstrap_info * dpp_add_nfc_uri(struct dpp_global *dpp,
+					    const char *uri)
+{
+	struct dpp_bootstrap_info *bi;
+
+	if (!dpp)
+		return NULL;
+
+	bi = dpp_parse_uri(uri);
+	if (!bi)
+		return NULL;
+
+	bi->type = DPP_BOOTSTRAP_NFC_URI;
 	bi->id = dpp_next_id(dpp);
 	dl_list_add(&dpp->bootstrap, &bi->list);
 	return bi;
@@ -8948,6 +9025,8 @@
 		bi->type = DPP_BOOTSTRAP_QR_CODE;
 	else if (os_strstr(cmd, "type=pkex"))
 		bi->type = DPP_BOOTSTRAP_PKEX;
+	else if (os_strstr(cmd, "type=nfc-uri"))
+		bi->type = DPP_BOOTSTRAP_NFC_URI;
 	else
 		goto fail;
 
diff --git a/src/common/dpp.h b/src/common/dpp.h
index abed3e4..cb788ae 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -106,6 +106,7 @@
 enum dpp_bootstrap_type {
 	DPP_BOOTSTRAP_QR_CODE,
 	DPP_BOOTSTRAP_PKEX,
+	DPP_BOOTSTRAP_NFC_URI,
 };
 
 struct dpp_bootstrap_info {
@@ -164,11 +165,13 @@
 enum dpp_netrole {
 	DPP_NETROLE_STA,
 	DPP_NETROLE_AP,
+	DPP_NETROLE_CONFIGURATOR,
 };
 
 struct dpp_configuration {
 	u8 ssid[32];
 	size_t ssid_len;
+	int ssid_charset;
 	enum dpp_akm akm;
 	enum dpp_netrole netrole;
 
@@ -248,6 +251,7 @@
 		char *connector; /* received signedConnector */
 		u8 ssid[SSID_MAX_LEN];
 		u8 ssid_len;
+		int ssid_charset;
 		char passphrase[64];
 		u8 psk[PMK_LEN];
 		int psk_set;
@@ -415,7 +419,6 @@
 			    const char *chan_list);
 int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac);
 int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info);
-struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri);
 char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
 		  const u8 *privkey, size_t privkey_len);
 struct hostapd_hw_modes;
@@ -438,7 +441,8 @@
 struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
 				   const char *json);
 struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
-					  const char *name, int netrole_ap,
+					  const char *name,
+					  enum dpp_netrole netrole,
 					  const char *mud_url, int *opclasses);
 int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
 		     const u8 *attr_start, size_t attr_len);
@@ -535,6 +539,8 @@
 
 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,
+					    const char *uri);
 int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd);
 struct dpp_bootstrap_info *
 dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id);
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 1ad8d7c..19593a5 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -40,23 +40,32 @@
 }
 
 
-struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
-						  int freq, int *chan)
+struct hostapd_channel_data *
+hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan,
+		    struct hostapd_hw_modes *hw_features, int num_hw_features)
 {
-	int i;
+	int i, j;
 
 	if (chan)
 		*chan = 0;
 
-	if (!mode)
+	if (!hw_features)
 		return NULL;
 
-	for (i = 0; i < mode->num_channels; i++) {
-		struct hostapd_channel_data *ch = &mode->channels[i];
-		if (ch->freq == freq) {
-			if (chan)
-				*chan = ch->chan;
-			return ch;
+	for (j = 0; j < num_hw_features; j++) {
+		struct hostapd_hw_modes *curr_mode = &hw_features[j];
+
+		if (curr_mode->mode != mode)
+			continue;
+		for (i = 0; i < curr_mode->num_channels; i++) {
+			struct hostapd_channel_data *ch =
+				&curr_mode->channels[i];
+
+			if (ch->freq == freq) {
+				if (chan)
+					*chan = ch->chan;
+				return ch;
+			}
 		}
 	}
 
@@ -74,29 +83,33 @@
 }
 
 
-int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
+int hw_get_chan(enum hostapd_hw_mode mode, int freq,
+		struct hostapd_hw_modes *hw_features, int num_hw_features)
 {
 	int chan;
 
-	hw_get_channel_freq(mode, freq, &chan);
+	hw_get_channel_freq(mode, freq, &chan, hw_features, num_hw_features);
 
 	return chan;
 }
 
 
-int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
-			      int sec_chan)
+int allowed_ht40_channel_pair(enum hostapd_hw_mode mode,
+			      struct hostapd_channel_data *p_chan,
+			      struct hostapd_channel_data *s_chan)
 {
 	int ok, first;
 	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
 			  149, 157, 165, 184, 192 };
 	size_t k;
-	struct hostapd_channel_data *p_chan, *s_chan;
-	const int ht40_plus = pri_chan < sec_chan;
+	int ht40_plus, pri_chan, sec_chan;
 
-	p_chan = hw_get_channel_chan(mode, pri_chan, NULL);
-	if (!p_chan)
+	if (!p_chan || !s_chan)
 		return 0;
+	pri_chan = p_chan->chan;
+	sec_chan = s_chan->chan;
+
+	ht40_plus = pri_chan < sec_chan;
 
 	if (pri_chan == sec_chan || !sec_chan) {
 		if (chan_pri_allowed(p_chan))
@@ -107,13 +120,9 @@
 		return 0;
 	}
 
-	s_chan = hw_get_channel_chan(mode, sec_chan, NULL);
-	if (!s_chan)
-		return 0;
-
 	wpa_printf(MSG_DEBUG,
-		   "HT40: control channel: %d  secondary channel: %d",
-		   pri_chan, sec_chan);
+		   "HT40: control channel: %d (%d MHz), secondary channel: %d (%d MHz)",
+		   pri_chan, p_chan->freq, sec_chan, s_chan->freq);
 
 	/* Verify that HT40 secondary channel is an allowed 20 MHz
 	 * channel */
@@ -131,7 +140,7 @@
 	 * 2.4 GHz rules allow all cases where the secondary channel fits into
 	 * the list of allowed channels (already checked above).
 	 */
-	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+	if (mode != HOSTAPD_MODE_IEEE80211A)
 		return 1;
 
 	first = pri_chan < sec_chan ? pri_chan : sec_chan;
@@ -176,22 +185,19 @@
 }
 
 
-int check_40mhz_5g(struct hostapd_hw_modes *mode,
-		   struct wpa_scan_results *scan_res, int pri_chan,
-		   int sec_chan)
+int check_40mhz_5g(struct wpa_scan_results *scan_res,
+		   struct hostapd_channel_data *pri_chan,
+		   struct hostapd_channel_data *sec_chan)
 {
-	int pri_freq, sec_freq, pri_bss, sec_bss;
+	int pri_bss, sec_bss;
 	int bss_pri_chan, bss_sec_chan;
 	size_t i;
 	int match;
 
-	if (!mode || !scan_res || !pri_chan || !sec_chan ||
-	    pri_chan == sec_chan)
+	if (!scan_res || !pri_chan || !sec_chan ||
+	    pri_chan->freq == sec_chan->freq)
 		return 0;
 
-	pri_freq = hw_get_freq(mode, pri_chan);
-	sec_freq = hw_get_freq(mode, sec_chan);
-
 	/*
 	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
 	 * channel, but not on selected PRI channel.
@@ -199,9 +205,9 @@
 	pri_bss = sec_bss = 0;
 	for (i = 0; i < scan_res->num; i++) {
 		struct wpa_scan_res *bss = scan_res->res[i];
-		if (bss->freq == pri_freq)
+		if (bss->freq == pri_chan->freq)
 			pri_bss++;
-		else if (bss->freq == sec_freq)
+		else if (bss->freq == sec_chan->freq)
 			sec_bss++;
 	}
 	if (sec_bss && !pri_bss) {
@@ -219,8 +225,8 @@
 	for (i = 0; i < scan_res->num; i++) {
 		struct wpa_scan_res *bss = scan_res->res[i];
 		get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
-		if (pri_chan == bss_pri_chan &&
-		    sec_chan == bss_sec_chan) {
+		if (pri_chan->chan == bss_pri_chan &&
+		    sec_chan->chan == bss_sec_chan) {
 			match = 1;
 			break;
 		}
@@ -229,8 +235,8 @@
 		for (i = 0; i < scan_res->num; i++) {
 			struct wpa_scan_res *bss = scan_res->res[i];
 			get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
-			if (pri_chan == bss_sec_chan &&
-			    sec_chan == bss_pri_chan) {
+			if (pri_chan->chan == bss_sec_chan &&
+			    sec_chan->chan == bss_pri_chan) {
 				wpa_printf(MSG_INFO, "Switch own primary and "
 					   "secondary channel due to BSS "
 					   "overlap with " MACSTR,
@@ -273,12 +279,87 @@
 }
 
 
+/*
+ * Returns:
+ * 0: no impact
+ * 1: overlapping BSS
+ * 2: overlapping BSS with 40 MHz intolerant advertisement
+ */
+int check_bss_coex_40mhz(struct wpa_scan_res *bss, int pri_freq, int sec_freq)
+{
+	int affected_start, affected_end;
+	struct ieee802_11_elems elems;
+	int pri_chan, sec_chan;
+	int pri = bss->freq;
+	int sec = pri;
+
+	if (pri_freq == sec_freq)
+		return 1;
+
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+
+	/* Check for overlapping 20 MHz BSS */
+	if (check_20mhz_bss(bss, pri_freq, affected_start, affected_end)) {
+		wpa_printf(MSG_DEBUG, "Overlapping 20 MHz BSS is found");
+		return 1;
+	}
+
+	get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+
+	if (sec_chan) {
+		if (sec_chan < pri_chan)
+			sec = pri - 20;
+		else
+			sec = pri + 20;
+	}
+
+	if ((pri < affected_start || pri > affected_end) &&
+	    (sec < affected_start || sec > affected_end))
+		return 0; /* not within affected channel range */
+
+	wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
+		   " freq=%d pri=%d sec=%d",
+		   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
+
+	if (sec_chan) {
+		if (pri_freq != pri || sec_freq != sec) {
+			wpa_printf(MSG_DEBUG,
+				   "40 MHz pri/sec mismatch with BSS "
+				   MACSTR
+				   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
+				   MAC2STR(bss->bssid),
+				   pri, sec, pri_chan,
+				   sec > pri ? '+' : '-',
+				   pri_freq, sec_freq);
+			return 1;
+		}
+	}
+
+	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+	if (elems.ht_capabilities) {
+		struct ieee80211_ht_capabilities *ht_cap =
+			(struct ieee80211_ht_capabilities *)
+			elems.ht_capabilities;
+
+		if (le_to_host16(ht_cap->ht_capabilities_info) &
+		    HT_CAP_INFO_40MHZ_INTOLERANT) {
+			wpa_printf(MSG_DEBUG,
+				   "40 MHz Intolerant is set on channel %d in BSS "
+				   MACSTR, pri, MAC2STR(bss->bssid));
+			return 2;
+		}
+	}
+
+	return 0;
+}
+
+
 int check_40mhz_2g4(struct hostapd_hw_modes *mode,
 		    struct wpa_scan_results *scan_res, int pri_chan,
 		    int sec_chan)
 {
 	int pri_freq, sec_freq;
-	int affected_start, affected_end;
 	size_t i;
 
 	if (!mode || !scan_res || !pri_chan || !sec_chan ||
@@ -288,70 +369,12 @@
 	pri_freq = hw_get_freq(mode, pri_chan);
 	sec_freq = hw_get_freq(mode, sec_chan);
 
-	affected_start = (pri_freq + sec_freq) / 2 - 25;
-	affected_end = (pri_freq + sec_freq) / 2 + 25;
 	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
-		   affected_start, affected_end);
+		   (pri_freq + sec_freq) / 2 - 25,
+		   (pri_freq + sec_freq) / 2 + 25);
 	for (i = 0; i < scan_res->num; i++) {
-		struct wpa_scan_res *bss = scan_res->res[i];
-		int pri = bss->freq;
-		int sec = pri;
-		struct ieee802_11_elems elems;
-
-		/* Check for overlapping 20 MHz BSS */
-		if (check_20mhz_bss(bss, pri_freq, affected_start,
-				    affected_end)) {
-			wpa_printf(MSG_DEBUG,
-				   "Overlapping 20 MHz BSS is found");
+		if (check_bss_coex_40mhz(scan_res->res[i], pri_freq, sec_freq))
 			return 0;
-		}
-
-		get_pri_sec_chan(bss, &pri_chan, &sec_chan);
-
-		if (sec_chan) {
-			if (sec_chan < pri_chan)
-				sec = pri - 20;
-			else
-				sec = pri + 20;
-		}
-
-		if ((pri < affected_start || pri > affected_end) &&
-		    (sec < affected_start || sec > affected_end))
-			continue; /* not within affected channel range */
-
-		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
-			   " freq=%d pri=%d sec=%d",
-			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
-
-		if (sec_chan) {
-			if (pri_freq != pri || sec_freq != sec) {
-				wpa_printf(MSG_DEBUG,
-					   "40 MHz pri/sec mismatch with BSS "
-					   MACSTR
-					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
-					   MAC2STR(bss->bssid),
-					   pri, sec, pri_chan,
-					   sec > pri ? '+' : '-',
-					   pri_freq, sec_freq);
-				return 0;
-			}
-		}
-
-		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
-				       0);
-		if (elems.ht_capabilities) {
-			struct ieee80211_ht_capabilities *ht_cap =
-				(struct ieee80211_ht_capabilities *)
-				elems.ht_capabilities;
-
-			if (le_to_host16(ht_cap->ht_capabilities_info) &
-			    HT_CAP_INFO_40MHZ_INTOLERANT) {
-				wpa_printf(MSG_DEBUG,
-					   "40 MHz Intolerant is set on channel %d in BSS "
-					   MACSTR, pri, MAC2STR(bss->bssid));
-				return 0;
-			}
-		}
 	}
 
 	return 1;
@@ -445,6 +468,8 @@
 			data->bandwidth = (1 << (u8) bw) * 20;
 			data->center_freq1 = freq1;
 			data->center_freq2 = freq2;
+			data->ht_enabled = 0;
+			data->vht_enabled = 0;
 		}
 
 		return 0;
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index c86e195..e57a8d6 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -14,18 +14,22 @@
 
 struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
 						  int chan, int *freq);
-struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
-						  int freq, int *chan);
+struct hostapd_channel_data *
+hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan,
+		    struct hostapd_hw_modes *hw_features, int num_hw_features);
 
 int hw_get_freq(struct hostapd_hw_modes *mode, int chan);
-int hw_get_chan(struct hostapd_hw_modes *mode, int freq);
+int hw_get_chan(enum hostapd_hw_mode mode, int freq,
+		struct hostapd_hw_modes *hw_features, int num_hw_features);
 
-int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
-			      int sec_chan);
+int allowed_ht40_channel_pair(enum hostapd_hw_mode mode,
+			      struct hostapd_channel_data *p_chan,
+			      struct hostapd_channel_data *s_chan);
 void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan);
-int check_40mhz_5g(struct hostapd_hw_modes *mode,
-		   struct wpa_scan_results *scan_res, int pri_chan,
-		   int sec_chan);
+int check_40mhz_5g(struct wpa_scan_results *scan_res,
+		   struct hostapd_channel_data *pri_chan,
+		   struct hostapd_channel_data *sec_chan);
+int check_bss_coex_40mhz(struct wpa_scan_res *bss, int pri_freq, int sec_freq);
 int check_40mhz_2g4(struct hostapd_hw_modes *mode,
 		    struct wpa_scan_results *scan_res, int pri_chan,
 		    int sec_chan);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index c6e6440..0d37674 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -722,13 +722,14 @@
  * for HT40 and VHT. DFS channels are not covered.
  * @freq: Frequency (MHz) to convert
  * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
- * @vht: VHT channel width (CHANWIDTH_*)
+ * @chanwidth: VHT/EDMG channel width (CHANWIDTH_*)
  * @op_class: Buffer for returning operating class
  * @channel: Buffer for returning channel number
  * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
  */
 enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
-						   int sec_channel, int vht,
+						   int sec_channel,
+						   int chanwidth,
 						   u8 *op_class, u8 *channel)
 {
 	u8 vht_opclass;
@@ -742,7 +743,7 @@
 		if ((freq - 2407) % 5)
 			return NUM_HOSTAPD_MODES;
 
-		if (vht)
+		if (chanwidth)
 			return NUM_HOSTAPD_MODES;
 
 		/* 2.407 GHz, channels 1..13 */
@@ -759,7 +760,7 @@
 	}
 
 	if (freq == 2484) {
-		if (sec_channel || vht)
+		if (sec_channel || chanwidth)
 			return NUM_HOSTAPD_MODES;
 
 		*op_class = 82; /* channel 14 */
@@ -776,7 +777,7 @@
 		return HOSTAPD_MODE_IEEE80211A;
 	}
 
-	switch (vht) {
+	switch (chanwidth) {
 	case CHANWIDTH_80MHZ:
 		vht_opclass = 128;
 		break;
@@ -877,17 +878,6 @@
 		return HOSTAPD_MODE_IEEE80211A;
 	}
 
-	/* 56.16 GHz, channel 1..6 */
-	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) {
-		if (sec_channel || vht)
-			return NUM_HOSTAPD_MODES;
-
-		*channel = (freq - 56160) / 2160;
-		*op_class = 180;
-
-		return HOSTAPD_MODE_IEEE80211AD;
-	}
-
 	if (freq > 5940 && freq <= 7105) {
 		int bw;
 		u8 idx = (freq - 5940) / 5;
@@ -901,6 +891,48 @@
 		return HOSTAPD_MODE_IEEE80211A;
 	}
 
+	/* 56.16 GHz, channel 1..6 */
+	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) {
+		if (sec_channel)
+			return NUM_HOSTAPD_MODES;
+
+		switch (chanwidth) {
+		case CHANWIDTH_USE_HT:
+		case CHANWIDTH_2160MHZ:
+			*channel = (freq - 56160) / 2160;
+			*op_class = 180;
+			break;
+		case CHANWIDTH_4320MHZ:
+			/* EDMG channels 9 - 13 */
+			if (freq > 56160 + 2160 * 5)
+				return NUM_HOSTAPD_MODES;
+
+			*channel = (freq - 56160) / 2160 + 8;
+			*op_class = 181;
+			break;
+		case CHANWIDTH_6480MHZ:
+			/* EDMG channels 17 - 20 */
+			if (freq > 56160 + 2160 * 4)
+				return NUM_HOSTAPD_MODES;
+
+			*channel = (freq - 56160) / 2160 + 16;
+			*op_class = 182;
+			break;
+		case CHANWIDTH_8640MHZ:
+			/* EDMG channels 25 - 27 */
+			if (freq > 56160 + 2160 * 3)
+				return NUM_HOSTAPD_MODES;
+
+			*channel = (freq - 56160) / 2160 + 24;
+			*op_class = 183;
+			break;
+		default:
+			return NUM_HOSTAPD_MODES;
+		}
+
+		return HOSTAPD_MODE_IEEE80211AD;
+	}
+
 	return NUM_HOSTAPD_MODES;
 }
 
@@ -908,27 +940,39 @@
 int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
 				  int sec_channel, u8 *op_class, u8 *channel)
 {
-	int vht = CHAN_WIDTH_UNKNOWN;
+	int cw = CHAN_WIDTH_UNKNOWN;
 
 	switch (chanwidth) {
 	case CHAN_WIDTH_UNKNOWN:
 	case CHAN_WIDTH_20_NOHT:
 	case CHAN_WIDTH_20:
 	case CHAN_WIDTH_40:
-		vht = CHANWIDTH_USE_HT;
+		cw = CHANWIDTH_USE_HT;
 		break;
 	case CHAN_WIDTH_80:
-		vht = CHANWIDTH_80MHZ;
+		cw = CHANWIDTH_80MHZ;
 		break;
 	case CHAN_WIDTH_80P80:
-		vht = CHANWIDTH_80P80MHZ;
+		cw = CHANWIDTH_80P80MHZ;
 		break;
 	case CHAN_WIDTH_160:
-		vht = CHANWIDTH_160MHZ;
+		cw = CHANWIDTH_160MHZ;
+		break;
+	case CHAN_WIDTH_2160:
+		cw = CHANWIDTH_2160MHZ;
+		break;
+	case CHAN_WIDTH_4320:
+		cw = CHANWIDTH_4320MHZ;
+		break;
+	case CHAN_WIDTH_6480:
+		cw = CHANWIDTH_6480MHZ;
+		break;
+	case CHAN_WIDTH_8640:
+		cw = CHANWIDTH_8640MHZ;
 		break;
 	}
 
-	if (ieee80211_freq_to_channel_ext(freq, sec_channel, vht, op_class,
+	if (ieee80211_freq_to_channel_ext(freq, sec_channel, cw, op_class,
 					  channel) == NUM_HOSTAPD_MODES) {
 		wpa_printf(MSG_WARNING,
 			   "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)",
@@ -1010,10 +1054,22 @@
 		if (chan < 149 || chan > 165)
 			return -1;
 		return 5000 + 5 * chan;
-	case 34: /* 60 GHz band, channels 1..6 */
-		if (chan < 1 || chan > 6)
+	case 34: /* 60 GHz band, channels 1..8 */
+		if (chan < 1 || chan > 8)
 			return -1;
 		return 56160 + 2160 * chan;
+	case 37: /* 60 GHz band, EDMG CB2, channels 9..15 */
+		if (chan < 9 || chan > 15)
+			return -1;
+		return 56160 + 2160 * (chan - 8);
+	case 38: /* 60 GHz band, EDMG CB3, channels 17..22 */
+		if (chan < 17 || chan > 22)
+			return -1;
+		return 56160 + 2160 * (chan - 16);
+	case 39: /* 60 GHz band, EDMG CB4, channels 25..29 */
+		if (chan < 25 || chan > 29)
+			return -1;
+		return 56160 + 2160 * (chan - 24);
 	}
 	return -1;
 }
@@ -1052,6 +1108,18 @@
 		if (chan < 1 || chan > 6)
 			return -1;
 		return 56160 + 2160 * chan;
+	case 21: /* 60 GHz band, EDMG CB2, channels 9..11 */
+		if (chan < 9 || chan > 11)
+			return -1;
+		return 56160 + 2160 * (chan - 8);
+	case 22: /* 60 GHz band, EDMG CB3, channels 17..18 */
+		if (chan < 17 || chan > 18)
+			return -1;
+		return 56160 + 2160 * (chan - 16);
+	case 23: /* 60 GHz band, EDMG CB4, channels 25 */
+		if (chan != 25)
+			return -1;
+		return 56160 + 2160 * (chan - 24);
 	}
 	return -1;
 }
@@ -1096,6 +1164,18 @@
 		if (chan < 1 || chan > 6)
 			return -1;
 		return 56160 + 2160 * chan;
+	case 62: /* 60 GHz band, EDMG CB2, channels 9..11 */
+		if (chan < 9 || chan > 11)
+			return -1;
+		return 56160 + 2160 * (chan - 8);
+	case 63: /* 60 GHz band, EDMG CB3, channels 17..18 */
+		if (chan < 17 || chan > 18)
+			return -1;
+		return 56160 + 2160 * (chan - 16);
+	case 64: /* 60 GHz band, EDMG CB4, channel 25 */
+		if (chan != 25)
+			return -1;
+		return 56160 + 2160 * (chan - 24);
 	}
 	return -1;
 }
@@ -1188,10 +1268,22 @@
 		if (chan < 1 || chan > 233)
 			return -1;
 		return 5940 + chan * 5;
-	case 180: /* 60 GHz band, channels 1..6 */
-		if (chan < 1 || chan > 6)
+	case 180: /* 60 GHz band, channels 1..8 */
+		if (chan < 1 || chan > 8)
 			return -1;
 		return 56160 + 2160 * chan;
+	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
+		if (chan < 9 || chan > 15)
+			return -1;
+		return 56160 + 2160 * (chan - 8);
+	case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
+		if (chan < 17 || chan > 22)
+			return -1;
+		return 56160 + 2160 * (chan - 16);
+	case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
+		if (chan < 25 || chan > 29)
+			return -1;
+		return 56160 + 2160 * (chan - 24);
 	}
 	return -1;
 }
@@ -1619,7 +1711,16 @@
 	{ 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 },
-	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160, P2P_SUPP },
+
+	/*
+	 * IEEE Std 802.11ad-2012 and P802.ay/D5.0 60 GHz operating classes.
+	 * Class 180 has the legacy channels 1-6. Classes 181-183 include
+	 * channels which implement channel bonding features.
+	 */
+	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 6, 1, BW2160, P2P_SUPP },
+	{ 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 },
 	{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
 };
 
@@ -1970,6 +2071,26 @@
 }
 
 
+int is_6ghz_psc_frequency(int freq)
+{
+	int i;
+
+	if (!is_6ghz_freq(freq))
+		return 0;
+	if ((((freq - 5940) / 5) & 0x3) != 0x1)
+		return 0;
+
+	i = (freq - 5940 + 55) % 80;
+	if (i == 0)
+		i = (freq - 5940 + 55) / 80;
+
+	if (i >= 1 && i <= 15)
+		return 1;
+
+	return 0;
+}
+
+
 int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
 				    size_t nei_rep_len)
 {
@@ -2150,3 +2271,121 @@
 
 	return 1;
 }
+
+
+int op_class_to_bandwidth(u8 op_class)
+{
+	switch (op_class) {
+	case 81:
+	case 82:
+		return 20;
+	case 83: /* channels 1..9; 40 MHz */
+	case 84: /* channels 5..13; 40 MHz */
+		return 40;
+	case 115: /* channels 36,40,44,48; indoor only */
+		return 20;
+	case 116: /* channels 36,44; 40 MHz; indoor only */
+	case 117: /* channels 40,48; 40 MHz; indoor only */
+		return 40;
+	case 118: /* channels 52,56,60,64; dfs */
+		return 20;
+	case 119: /* channels 52,60; 40 MHz; dfs */
+	case 120: /* channels 56,64; 40 MHz; dfs */
+		return 40;
+	case 121: /* channels 100-140 */
+		return 20;
+	case 122: /* channels 100-142; 40 MHz */
+	case 123: /* channels 104-136; 40 MHz */
+		return 40;
+	case 124: /* channels 149,153,157,161 */
+	case 125: /* channels 149,153,157,161,165,169 */
+		return 20;
+	case 126: /* channels 149,157; 40 MHz */
+	case 127: /* channels 153,161; 40 MHz */
+		return 40;
+	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+		return 80;
+	case 129: /* center freqs 50, 114; 160 MHz */
+		return 160;
+	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80+80 MHz */
+		return 80;
+	case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */
+		return 20;
+	case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */
+		return 40;
+	case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
+		return 80;
+	case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
+	case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
+		return 160;
+	case 180: /* 60 GHz band, channels 1..8 */
+		return 2160;
+	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
+		return 4320;
+	case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
+		return 6480;
+	case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
+		return 8640;
+	}
+
+	return 20;
+}
+
+
+int op_class_to_ch_width(u8 op_class)
+{
+	switch (op_class) {
+	case 81:
+	case 82:
+		return CHANWIDTH_USE_HT;
+	case 83: /* channels 1..9; 40 MHz */
+	case 84: /* channels 5..13; 40 MHz */
+		return CHANWIDTH_USE_HT;
+	case 115: /* channels 36,40,44,48; indoor only */
+		return CHANWIDTH_USE_HT;
+	case 116: /* channels 36,44; 40 MHz; indoor only */
+	case 117: /* channels 40,48; 40 MHz; indoor only */
+		return CHANWIDTH_USE_HT;
+	case 118: /* channels 52,56,60,64; dfs */
+		return CHANWIDTH_USE_HT;
+	case 119: /* channels 52,60; 40 MHz; dfs */
+	case 120: /* channels 56,64; 40 MHz; dfs */
+		return CHANWIDTH_USE_HT;
+	case 121: /* channels 100-140 */
+		return CHANWIDTH_USE_HT;
+	case 122: /* channels 100-142; 40 MHz */
+	case 123: /* channels 104-136; 40 MHz */
+		return CHANWIDTH_USE_HT;
+	case 124: /* channels 149,153,157,161 */
+	case 125: /* channels 149,153,157,161,165,169 */
+		return CHANWIDTH_USE_HT;
+	case 126: /* channels 149,157; 40 MHz */
+	case 127: /* channels 153,161; 40 MHz */
+		return CHANWIDTH_USE_HT;
+	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+		return CHANWIDTH_80MHZ;
+	case 129: /* center freqs 50, 114; 160 MHz */
+		return CHANWIDTH_160MHZ;
+	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80+80 MHz */
+		return CHANWIDTH_80P80MHZ;
+	case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */
+		return CHANWIDTH_USE_HT;
+	case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */
+		return CHANWIDTH_USE_HT;
+	case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
+		return CHANWIDTH_80MHZ;
+	case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
+		return CHANWIDTH_160MHZ;
+	case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
+		return CHANWIDTH_80P80MHZ;
+	case 180: /* 60 GHz band, channels 1..8 */
+		return CHANWIDTH_2160MHZ;
+	case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
+		return CHANWIDTH_4320MHZ;
+	case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
+		return CHANWIDTH_6480MHZ;
+	case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
+		return CHANWIDTH_8640MHZ;
+	}
+	return CHANWIDTH_USE_HT;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 052f333..fbda10c 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -198,7 +198,8 @@
 	u8 min_chan;
 	u8 max_chan;
 	u8 inc;
-	enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80 } bw;
+	enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80, BW4320,
+	       BW6480, BW8640} bw;
 	enum { P2P_SUPP, NO_P2P_SUPP } p2p;
 };
 
@@ -225,11 +226,14 @@
 int center_idx_to_bw_6ghz(u8 idx);
 int is_6ghz_freq(int freq);
 int is_6ghz_op_class(u8 op_class);
+int is_6ghz_psc_frequency(int freq);
 
 int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
 				    size_t nei_rep_len);
 
 int ieee802_11_ext_capab(const u8 *ie, unsigned int capab);
+int op_class_to_bandwidth(u8 op_class);
+int op_class_to_ch_width(u8 op_class);
 
 /* element iteration helpers */
 #define for_each_element(_elem, _data, _datalen)			\
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index fbed051..e3c2ed3 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1285,11 +1285,15 @@
 
 #define VHT_RX_NSS_MAX_STREAMS			    8
 
-/* VHT channel widths */
+/* VHT/EDMG channel widths */
 #define CHANWIDTH_USE_HT	0
 #define CHANWIDTH_80MHZ		1
 #define CHANWIDTH_160MHZ	2
 #define CHANWIDTH_80P80MHZ	3
+#define CHANWIDTH_2160MHZ	4
+#define CHANWIDTH_4320MHZ	5
+#define CHANWIDTH_6480MHZ	6
+#define CHANWIDTH_8640MHZ	7
 
 #define HE_NSS_MAX_STREAMS			    8
 
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index a0a0fb5..778c217 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -609,6 +609,14 @@
  *	coex chain mode from application/service.
  *	The attributes defined in enum qca_vendor_attr_btc_chain_mode are used
  *	to deliver the parameters.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO: This vendor subcommand is used to
+ *	get information of a station from driver to userspace. This command can
+ *	be used in both STA and AP modes. For STA mode, it provides information
+ *	of the current association when in connected state or the last
+ *	association when in disconnected state. For AP mode, only information
+ *	of the currently connected stations is available. This command uses
+ *	attributes defined in enum qca_wlan_vendor_attr_get_sta_info.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -784,6 +792,7 @@
 	QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_EXT = 183,
 	QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE = 184,
 	QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE = 185,
+	QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO = 186,
 };
 
 enum qca_wlan_vendor_attr {
@@ -8046,4 +8055,110 @@
 	QCA_VENDOR_ATTR_BTC_CHAIN_MODE_LAST - 1,
 };
 
+/**
+ * enum qca_vendor_wlan_sta_flags - Station feature flags
+ * Bits will be set to 1 if the corresponding features are enabled.
+ * @QCA_VENDOR_WLAN_STA_FLAG_AMPDU: AMPDU is enabled for the station
+ * @QCA_VENDOR_WLAN_STA_FLAG_TX_STBC: TX Space-time block coding is enabled
+    for the station
+ * @QCA_VENDOR_WLAN_STA_FLAG_RX_STBC: RX Space-time block coding is enabled
+    for the station
+ */
+enum qca_vendor_wlan_sta_flags {
+	QCA_VENDOR_WLAN_STA_FLAG_AMPDU = BIT(0),
+	QCA_VENDOR_WLAN_STA_FLAG_TX_STBC = BIT(1),
+	QCA_VENDOR_WLAN_STA_FLAG_RX_STBC = BIT(2),
+};
+
+/**
+ * enum qca_vendor_wlan_sta_guard_interval - Station guard interval
+ * @QCA_VENDOR_WLAN_STA_GI_800_NS: Legacy normal guard interval
+ * @QCA_VENDOR_WLAN_STA_GI_400_NS: Legacy short guard interval
+ * @QCA_VENDOR_WLAN_STA_GI_1600_NS: Guard interval used by HE
+ * @QCA_VENDOR_WLAN_STA_GI_3200_NS: Guard interval used by HE
+ */
+enum qca_vendor_wlan_sta_guard_interval {
+	QCA_VENDOR_WLAN_STA_GI_800_NS = 0,
+	QCA_VENDOR_WLAN_STA_GI_400_NS = 1,
+	QCA_VENDOR_WLAN_STA_GI_1600_NS = 2,
+	QCA_VENDOR_WLAN_STA_GI_3200_NS = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_sta_info - Defines attributes
+ * used by QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC:
+ * Required attribute in request, 6-byte MAC address,
+ * used in both STA and AP modes.
+ * MAC address of the station for which information is requested (BSSID of the
+ * AP in STA mode).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_FLAGS:
+ * Optionally used in response, u32 attribute, contains a bitmap of different
+ * fields defined in enum qca_vendor_wlan_sta_flags, used in AP mode only.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_GUARD_INTERVAL:
+ * Optionally used in response, u32 attribute, possible values are defined in
+ * enum qca_vendor_wlan_sta_guard_interval, used in AP mode only.
+ * Guard interval used by the station.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_RETRY_COUNT:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Value indicates the number of data frames received from station with retry
+ * bit set to 1 in FC.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BC_MC_COUNT:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Counter for number of data frames with broadcast or multicast address in
+ * the destination address received from the station.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_SUCCEED:
+ * Optionally used in response, u32 attribute, used in both STA and AP modes.
+ * Value indicates the number of data frames successfully transmitted only
+ * after retrying the packets and for which the TX status has been updated
+ * back to host from target.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_EXHAUSTED:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Value indicates the number of data frames not transmitted successfully even
+ * after retrying the packets for the number of times equal to the total number
+ * of retries allowed for that packet and for which the TX status has been
+ * updated back to host from target.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_TOTAL:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Counter in the target for the number of data frames successfully transmitted
+ * to the station.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Value indicates the number of data frames successfully transmitted only
+ * after retrying the packets.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY_EXHAUSTED:
+ * Optionally used in response, u32 attribute, used in AP mode only.
+ * Value indicates the number of data frames not transmitted successfully even
+ * after retrying the packets for the number of times equal to the total number
+ * of retries allowed for that packet.
+ */
+enum qca_wlan_vendor_attr_get_sta_info {
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC = 1,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_FLAGS = 2,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_GUARD_INTERVAL = 3,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_RETRY_COUNT = 4,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BC_MC_COUNT = 5,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_SUCCEED = 6,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_EXHAUSTED = 7,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_TOTAL = 8,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY = 9,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY_EXHAUSTED = 10,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX =
+	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST - 1,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 2ab168b..bf8cc9d 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -579,20 +579,26 @@
 {
 	switch (group) {
 	case 19:
+		*z = -10;
+		return 0;
 	case 20:
+		*z = -12;
+		return 0;
 	case 21:
-	case 28:
-		*z = -2;
+		*z = -4;
 		return 0;
 	case 25:
 	case 29:
 		*z = -5;
 		return 0;
 	case 26:
-		*z = -11;
+		*z = 31;
+		return 0;
+	case 28:
+		*z = -2;
 		return 0;
 	case 30:
-		*z = 2;
+		*z = 7;
 		return 0;
 	}
 
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index ea9f7a2..de4b6ec 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -2200,7 +2200,7 @@
 				   "RSN: Remove %u old PMKID(s) from RSNE",
 				   num_pmkid);
 			after = rpos + 2 + num_pmkid * PMKID_LEN;
-			os_memmove(rpos + 2, after, rend - after);
+			os_memmove(rpos + 2, after, end - after);
 			start[1] -= num_pmkid * PMKID_LEN;
 			added -= num_pmkid * PMKID_LEN;
 		}
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 70ecf5d..f03c698 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -171,6 +171,7 @@
 #define DPP_EVENT_CONN_STATUS_RESULT "DPP-CONN-STATUS-RESULT "
 #define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM "
 #define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID "
+#define DPP_EVENT_CONFOBJ_SSID_CHARSET "DPP-CONFOBJ-SSID-CHARSET "
 #define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS "
 #define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
 #define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
@@ -300,6 +301,8 @@
 #define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED "
 #define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
 #define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
+#define WPS_EVENT_PIN_ACTIVE "WPS-PIN-ACTIVE "
+#define WPS_EVENT_CANCEL "WPS-CANCEL "
 #define AP_STA_CONNECTED "AP-STA-CONNECTED "
 #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
 #define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH "
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index f3803bd..84ec985 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -4065,6 +4065,7 @@
 	int cipher, digest;
 	const EVP_CIPHER *c;
 	const EVP_MD *h;
+	int mac_key_len, enc_key_len, fixed_iv_len;
 
 	ssl_cipher = SSL_get_current_cipher(ssl);
 	if (!ssl_cipher)
@@ -4075,17 +4076,33 @@
 		   cipher, digest);
 	if (cipher < 0 || digest < 0)
 		return -1;
-	c = EVP_get_cipherbynid(cipher);
-	h = EVP_get_digestbynid(digest);
-	if (!c || !h)
+	if (cipher == NID_undef) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: no cipher in use?!");
 		return -1;
+	}
+	c = EVP_get_cipherbynid(cipher);
+	if (!c)
+		return -1;
+	enc_key_len = EVP_CIPHER_key_length(c);
+	if (EVP_CIPHER_mode(c) == EVP_CIPH_GCM_MODE ||
+	    EVP_CIPHER_mode(c) == EVP_CIPH_CCM_MODE)
+		fixed_iv_len = 4; /* only part of IV from PRF */
+	else
+		fixed_iv_len = EVP_CIPHER_iv_length(c);
+	if (digest == NID_undef) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: no digest in use (e.g., AEAD)");
+		mac_key_len = 0;
+	} else {
+		h = EVP_get_digestbynid(digest);
+		if (!h)
+			return -1;
+		mac_key_len = EVP_MD_size(h);
+	}
 
 	wpa_printf(MSG_DEBUG,
-		   "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d",
-		   EVP_CIPHER_key_length(c), EVP_MD_size(h),
-		   EVP_CIPHER_iv_length(c));
-	return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) +
-		    EVP_CIPHER_iv_length(c));
+		   "OpenSSL: keyblock size: mac_key_len=%d enc_key_len=%d fixed_iv_len=%d",
+		   mac_key_len, enc_key_len, fixed_iv_len);
+	return 2 * (mac_key_len + enc_key_len + fixed_iv_len);
 #endif
 }
 #endif /* OPENSSL_NEED_EAP_FAST_PRF */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index ad68a07..f0e2a6f 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2192,9 +2192,7 @@
 	/* Configured ACS channel width */
 	u16 ch_width;
 
-	/* ACS channel list info */
-	unsigned int ch_list_len;
-	const u8 *ch_list;
+	/* ACS frequency list info */
 	const int *freq_list;
 };
 
@@ -5609,8 +5607,8 @@
 
 	/**
 	 * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
-	 * @pri_channel: Selected primary channel
-	 * @sec_channel: Selected secondary channel
+	 * @pri_freq: Selected primary frequency
+	 * @sec_freq: Selected secondary frequency
 	 * @vht_seg0_center_ch: VHT mode Segment0 center channel
 	 * @vht_seg1_center_ch: VHT mode Segment1 center channel
 	 * @ch_width: Selected Channel width by driver. Driver may choose to
@@ -5619,8 +5617,8 @@
 	 * hw_mode: Selected band (used with hw_mode=any)
 	 */
 	struct acs_selected_channels {
-		u8 pri_channel;
-		u8 sec_channel;
+		unsigned int pri_freq;
+		unsigned int sec_freq;
 		u8 vht_seg0_center_ch;
 		u8 vht_seg1_center_ch;
 		u16 ch_width;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 4c8dcad..b7efb6a 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -6993,10 +6993,14 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
 		   ifname, brname);
 	if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
-		wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
-			   "into bridge %s: %s",
+		wpa_printf(MSG_WARNING,
+			   "nl80211: Failed to add interface %s into bridge %s: %s",
 			   ifname, brname, strerror(errno));
-		return -1;
+		/* Try to continue without the interface being in a bridge. This
+		 * may be needed for some cases, e.g., with Open vSwitch, where
+		 * an external component will need to handle bridge
+		 * configuration. */
+		return 0;
 	}
 	bss->added_if_into_bridge = 1;
 
@@ -10157,6 +10161,48 @@
 }
 
 
+static int add_acs_ch_list(struct nl_msg *msg, const int *freq_list)
+{
+	int num_channels = 0, num_freqs;
+	u8 *ch_list;
+	enum hostapd_hw_mode hw_mode;
+	int ret = 0;
+	int i;
+
+	if (!freq_list)
+		return 0;
+
+	num_freqs = int_array_len(freq_list);
+	ch_list = os_malloc(sizeof(u8) * num_freqs);
+	if (!ch_list)
+		return -1;
+
+	for (i = 0; i < num_freqs; i++) {
+		const int freq = freq_list[i];
+
+		if (freq == 0)
+			break;
+		/* Send 2.4 GHz and 5 GHz channels with
+		 * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST to maintain backwards
+		 * compatibility.
+		 */
+		if (!(freq >= 2412 && freq <= 2484) &&
+		    !(freq >= 5180 && freq <= 5900))
+			continue;
+		hw_mode = ieee80211_freq_to_chan(freq, &ch_list[num_channels]);
+		if (hw_mode != NUM_HOSTAPD_MODES)
+			num_channels++;
+	}
+
+	if (num_channels)
+		ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
+			      num_channels, ch_list);
+
+	os_free(ch_list);
+	return ret;
+}
+
+
 static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
 {
 	int i, len, ret;
@@ -10204,9 +10250,7 @@
 	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) ||
 	    nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
 			params->ch_width) ||
-	    (params->ch_list_len &&
-	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
-		     params->ch_list)) ||
+	    add_acs_ch_list(msg, params->freq_list) ||
 	    add_acs_freq_list(msg, params->freq_list)) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
@@ -10214,9 +10258,9 @@
 	nla_nest_end(msg, data);
 
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d CH_LIST_LEN: %u",
+		   "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d",
 		   params->hw_mode, params->ht_enabled, params->ht40_enabled,
-		   params->vht_enabled, params->ch_width, params->ch_list_len);
+		   params->vht_enabled, params->ch_width);
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret) {
@@ -11051,8 +11095,8 @@
 	int ret = -1;
 
 	/* External auth command/status is intended for drivers that implement
-	 * intenral SME but want to offload authentication processing (e.g.,
-	 * SAE) to hostapd/wpa_supplicant. Do nott send the status to drivers
+	 * internal SME but want to offload authentication processing (e.g.,
+	 * SAE) to hostapd/wpa_supplicant. Do not send the status to drivers
 	 * which do not support AP SME or use wpa_supplicant/hostapd SME.
 	 */
 	if ((is_ap_interface(drv->nlmode) && !bss->drv->device_ap_sme) ||
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index d8630bb..9a82cd1 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -1365,12 +1365,18 @@
 			  struct nlattr *tb_freq[])
 {
 	u8 channel;
+
+	os_memset(chan, 0, sizeof(*chan));
 	chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
 	chan->flag = 0;
 	chan->allowed_bw = ~0;
 	chan->dfs_cac_ms = 0;
 	if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
 		chan->chan = channel;
+	else
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: No channel number found for frequency %u MHz",
+			   chan->freq);
 
 	if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
 		chan->flag |= HOSTAPD_CHAN_DISABLED;
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 7c16330..2aecc74 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -524,6 +524,10 @@
 		break;
 	case CHAN_WIDTH_UNKNOWN:
 	case CHAN_WIDTH_80P80:
+	case CHAN_WIDTH_2160:
+	case CHAN_WIDTH_4320:
+	case CHAN_WIDTH_6480:
+	case CHAN_WIDTH_8640:
 		/* FIXME: implement this */
 		return 0;
 	}
@@ -1736,26 +1740,57 @@
 }
 
 
+static unsigned int chan_2ghz_or_5ghz_to_freq(u8 chan)
+{
+	if (chan >= 1 && chan <= 13)
+		return 2407 + 5 * chan;
+	if (chan == 14)
+		return 2484;
+	if (chan >= 36 && chan <= 169)
+		return 5000 + 5 * chan;
+
+	return 0;
+}
+
+
 static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
 				   const u8 *data, size_t len)
 {
 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
 	union wpa_event_data event;
+	u8 chan;
 
 	wpa_printf(MSG_DEBUG,
 		   "nl80211: ACS channel selection vendor event received");
 
 	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX,
 		      (struct nlattr *) data, len, NULL) ||
-	    !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] ||
-	    !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL])
+	    (!tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY] &&
+	     !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]) ||
+	    (!tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY] &&
+	     !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]))
 		return;
 
 	os_memset(&event, 0, sizeof(event));
-	event.acs_selected_channels.pri_channel =
-		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
-	event.acs_selected_channels.sec_channel =
-		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY]) {
+		event.acs_selected_channels.pri_freq = nla_get_u32(
+			tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY]);
+	} else {
+		chan = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
+		event.acs_selected_channels.pri_freq =
+			chan_2ghz_or_5ghz_to_freq(chan);
+	}
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY]) {
+		event.acs_selected_channels.sec_freq = nla_get_u32(
+			tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY]);
+	} else {
+		chan = nla_get_u8(
+			tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+		event.acs_selected_channels.sec_freq =
+			chan_2ghz_or_5ghz_to_freq(chan);
+	}
+
 	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
 		event.acs_selected_channels.vht_seg0_center_ch =
 			nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
@@ -1780,9 +1815,9 @@
 	}
 
 	wpa_printf(MSG_INFO,
-		   "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d",
-		   event.acs_selected_channels.pri_channel,
-		   event.acs_selected_channels.sec_channel,
+		   "nl80211: ACS Results: PFreq: %d SFreq: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d",
+		   event.acs_selected_channels.pri_freq,
+		   event.acs_selected_channels.sec_freq,
 		   event.acs_selected_channels.ch_width,
 		   event.acs_selected_channels.vht_seg0_center_ch,
 		   event.acs_selected_channels.vht_seg1_center_ch,
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 442c59c..bc2e87e 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -140,10 +140,6 @@
 DRV_OBJS += ../src/drivers/netlink.o
 endif
 
-ifdef NEED_LINUX_IOCTL
-DRV_OBJS += ../src/drivers/linux_ioctl.o
-endif
-
 ifdef NEED_RFKILL
 DRV_OBJS += ../src/drivers/rfkill.o
 endif
@@ -152,13 +148,18 @@
 DRV_OBJS += ../src/utils/radiotap.o
 endif
 
-ifdef CONFIG_VLAN_NETLINK
 ifdef CONFIG_FULL_DYNAMIC_VLAN
+NEED_LINUX_IOCTL=y
+ifdef CONFIG_VLAN_NETLINK
 NEED_LIBNL=y
 CONFIG_LIBNL3_ROUTE=y
 endif
 endif
 
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += ../src/drivers/linux_ioctl.o
+endif
+
 ifdef NEED_LIBNL
 ifndef CONFIG_LIBNL32
 ifndef CONFIG_LIBNL20
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 599a0b5..c3c2c0f 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -132,10 +132,6 @@
 DRV_OBJS += src/drivers/netlink.c
 endif
 
-ifdef NEED_LINUX_IOCTL
-DRV_OBJS += src/drivers/linux_ioctl.c
-endif
-
 ifdef NEED_RFKILL
 DRV_OBJS += src/drivers/rfkill.c
 endif
@@ -148,13 +144,18 @@
 DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM
 endif
 
-ifdef CONFIG_VLAN_NETLINK
 ifdef CONFIG_FULL_DYNAMIC_VLAN
+NEED_LINUX_IOCTL=y
+ifdef CONFIG_VLAN_NETLINK
 NEED_LIBNL=y
 CONFIG_LIBNL3_ROUTE=y
 endif
 endif
 
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += src/drivers/linux_ioctl.c
+endif
+
 ifdef NEED_LIBNL
 ifdef CONFIG_LIBNL32
   DRV_LIBS += -lnl-3
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index beee59c..341e0e8 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -249,6 +249,22 @@
  */
 
 /**
+ * DOC: VLAN offload support for setting group keys and binding STAs to VLANs
+ *
+ * By setting @NL80211_EXT_FEATURE_VLAN_OFFLOAD flag drivers can indicate they
+ * support offloading VLAN functionality in a manner where the driver exposes a
+ * single netdev that uses VLAN tagged frames and separate VLAN-specific netdevs
+ * can then be added using RTM_NEWLINK/IFLA_VLAN_ID similarly to the Ethernet
+ * case. Frames received from stations that are not assigned to any VLAN are
+ * delivered on the main netdev and frames to such stations can be sent through
+ * that main netdev.
+ *
+ * %NL80211_CMD_NEW_KEY (for group keys), %NL80211_CMD_NEW_STATION, and
+ * %NL80211_CMD_SET_STATION will optionally specify vlan_id using
+ * %NL80211_ATTR_VLAN_ID.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -571,6 +587,14 @@
  *	set of BSSID,frequency parameters is used (i.e., either the enforcing
  *	%NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
  *	%NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
+ *	Driver shall not modify the IEs specified through %NL80211_ATTR_IE if
+ *	%NL80211_ATTR_MAC is included. However, if %NL80211_ATTR_MAC_HINT is
+ *	included, these IEs through %NL80211_ATTR_IE are specified by the user
+ *	space based on the best possible BSS selected. Thus, if the driver ends
+ *	up selecting a different BSS, it can modify these IEs accordingly (e.g.
+ *	userspace asks the driver to perform PMKSA caching with BSS1 and the
+ *	driver ends up selecting BSS2 with different PMKSA cache entry; RSNIE
+ *	has to get updated with the apt PMKID).
  *	%NL80211_ATTR_PREV_BSSID can be used to request a reassociation within
  *	the ESS in case the device is already associated and an association with
  *	a different BSS is desired.
@@ -2373,6 +2397,9 @@
  *	the allowed channel bandwidth configurations. (u8 attribute)
  *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
  *
+ * @NL80211_ATTR_VLAN_ID: VLAN ID (1..4094) for the station and VLAN group key
+ *	(u16).
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2835,6 +2862,8 @@
 	NL80211_ATTR_WIPHY_EDMG_CHANNELS,
 	NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
 
+	NL80211_ATTR_VLAN_ID,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -5484,6 +5513,10 @@
  * @NL80211_EXT_FEATURE_SAE_OFFLOAD: Device wants to do SAE authentication in
  *	station mode (SAE password is passed as part of the connect command).
  *
+ * @NL80211_EXT_FEATURE_VLAN_OFFLOAD: The driver supports a single netdev
+ *	with VLAN tagged frames and separate VLAN-specific netdevs added using
+ *	vconfig similarly to the Ethernet case.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -5529,6 +5562,7 @@
 	NL80211_EXT_FEATURE_EXT_KEY_ID,
 	NL80211_EXT_FEATURE_STA_TX_PWR,
 	NL80211_EXT_FEATURE_SAE_OFFLOAD,
+	NL80211_EXT_FEATURE_VLAN_OFFLOAD,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 2ea4efd..dd9848e 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -44,7 +44,7 @@
 	u8 *last_eap_identity;
 	size_t last_eap_identity_len;
 	enum {
-		CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
+		CONTINUE, START_DONE, RESULT_SUCCESS, SUCCESS, FAILURE
 	} state;
 	int result_ind, use_result_ind;
 	int use_pseudonym;
@@ -58,6 +58,8 @@
 	switch (state) {
 	case CONTINUE:
 		return "CONTINUE";
+	case START_DONE:
+		return "START_DONE";
 	case RESULT_SUCCESS:
 		return "RESULT_SUCCESS";
 	case SUCCESS:
@@ -486,6 +488,7 @@
 	const u8 *identity = NULL;
 	size_t identity_len = 0;
 	struct eap_sim_msg *msg;
+	struct wpabuf *resp;
 
 	data->reauth = 0;
 	if (id_req == ANY_ID && data->reauth_id) {
@@ -535,7 +538,10 @@
 				identity, identity_len);
 	}
 
-	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
+	resp = eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
+	if (resp)
+		eap_sim_state(data, START_DONE);
+	return resp;
 }
 
 
@@ -721,6 +727,13 @@
 	int res;
 
 	wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge");
+	if (data->state != START_DONE) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-SIM: Unexpected Challenge in state %s",
+			   eap_sim_state_txt(data->state));
+		return eap_sim_client_error(data, id,
+					    EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+	}
 	data->reauth = 0;
 	if (!attr->mac || !attr->rand) {
 		wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
index a9bafe2..c460980 100644
--- a/src/eap_peer/tncc.c
+++ b/src/eap_peer/tncc.c
@@ -144,7 +144,7 @@
 	TNC_MessageType messageType)
 {
 	struct tnc_if_imc *imc;
-	unsigned char *b64;
+	char *b64;
 	size_t b64len;
 
 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu "
@@ -629,8 +629,7 @@
 		return NULL;
 	*pos2 = '\0';
 
-	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
-				decoded_len);
+	decoded = base64_decode(pos, os_strlen(pos), decoded_len);
 	*pos2 = '<';
 	if (decoded == NULL) {
 		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c
index 942a195..4a30486 100644
--- a/src/eap_server/tncs.c
+++ b/src/eap_server/tncs.c
@@ -179,7 +179,7 @@
 	TNC_MessageType messageType)
 {
 	struct tncs_data *tncs;
-	unsigned char *b64;
+	char *b64;
 	size_t b64len;
 
 	wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
@@ -678,8 +678,7 @@
 		return NULL;
 	*pos2 = '\0';
 
-	decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
-				decoded_len);
+	decoded = base64_decode(pos, os_strlen(pos), decoded_len);
 	*pos2 = '<';
 	if (decoded == NULL) {
 		wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index 291c9dd..138dcaf 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -171,13 +171,16 @@
 		u8 hash[SHA1_MAC_LEN];
 		const u8 *addr[1];
 		size_t len[1];
+		const struct l2_ethhdr *eth = (const struct l2_ethhdr *) buf;
 
 		/*
 		 * Close the workaround socket if the kernel version seems to be
 		 * able to deliver packets through the packet socket before
 		 * authorization has been completed (in dormant state).
 		 */
-		if (l2->num_rx_br <= 1) {
+		if (l2->num_rx_br <= 1 &&
+		    (os_memcmp(eth->h_dest, l2->own_addr, ETH_ALEN) == 0 ||
+		     is_multicast_ether_addr(eth->h_dest))) {
 			wpa_printf(MSG_DEBUG,
 				   "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
 				   l2->ifname);
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 0396312..9c73885 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1404,6 +1404,7 @@
 	const int op_classes_5ghz[] = { 124, 125, 115, 0 };
 	const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
 	const int op_classes_vht[] = { 128, 0 };
+	const int op_classes_edmg[] = { 181, 182, 183, 0 };
 
 	p2p_dbg(p2p, "Prepare channel best");
 
@@ -1435,6 +1436,11 @@
 		p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference");
 		p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class;
 		p2p->op_channel = p2p->cfg->pref_chan[0].chan;
+	} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_edmg,
+				      &p2p->op_reg_class, &p2p->op_channel) ==
+		   0) {
+		p2p_dbg(p2p, "Select possible EDMG channel (op_class %u channel %u) as operating channel preference",
+			p2p->op_reg_class, p2p->op_channel);
 	} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht,
 				      &p2p->op_reg_class, &p2p->op_channel) ==
 		   0) {
@@ -4926,6 +4932,7 @@
 	res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
 				    buf, len, wait_time, &scheduled);
 	if (res == 0 && scheduled && p2p->in_listen && freq > 0 &&
+	    p2p->drv_in_listen > 0 &&
 	    (unsigned int) p2p->drv_in_listen != freq) {
 		p2p_dbg(p2p,
 			"Stop listen on %d MHz to allow a frame to be sent immediately on %d MHz",
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 425b037..79de09c 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -99,6 +99,8 @@
 
 	int vht;
 
+	int edmg;
+
 	u8 max_oper_chwidth;
 
 	unsigned int vht_center_freq2;
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index c94bf41..1133461 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -390,6 +390,7 @@
 	const int op_classes_5ghz[] = { 124, 125, 115, 0 };
 	const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
 	const int op_classes_vht[] = { 128, 129, 130, 0 };
+	const int op_classes_edmg[] = { 181, 182, 183, 0 };
 
 	if (p2p->own_freq_preference > 0 &&
 	    p2p_freq_to_channel(p2p->own_freq_preference,
@@ -454,6 +455,14 @@
 		}
 	}
 
+	/* Try a channel where we might be able to use EDMG */
+	if (p2p_channel_select(intersection, op_classes_edmg,
+			       &p2p->op_reg_class, &p2p->op_channel) == 0) {
+		p2p_dbg(p2p, "Pick possible EDMG channel (op_class %u channel %u) from intersection",
+			p2p->op_reg_class, p2p->op_channel);
+		return;
+	}
+
 	/* Try a channel where we might be able to use VHT */
 	if (p2p_channel_select(intersection, op_classes_vht,
 			       &p2p->op_reg_class, &p2p->op_channel) == 0) {
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 35120a1..fa5215c 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -280,7 +280,7 @@
  * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  * @network_ctx: Network configuration context or %NULL to flush all entries
- * @pmk: PMK to match for or %NYLL to match all PMKs
+ * @pmk: PMK to match for or %NULL to match all PMKs
  * @pmk_len: PMK length
  */
 void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index ace0b6a..892eece 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -488,6 +488,8 @@
 	if (wpa_key_mgmt_ft(sm->key_mgmt)) {
 		int res;
 
+		wpa_hexdump(MSG_DEBUG, "WPA: WPA IE before FT processing",
+			    wpa_ie, wpa_ie_len);
 		/*
 		 * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and
 		 * FTIE from (Re)Association Response.
@@ -503,8 +505,14 @@
 			os_free(rsn_ie_buf);
 			return -1;
 		}
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: WPA IE after PMKID[PMKR1Name] addition into RSNE",
+			    rsn_ie_buf, wpa_ie_len);
 
 		if (sm->assoc_resp_ies) {
+			wpa_hexdump(MSG_DEBUG, "WPA: Add assoc_resp_ies",
+				    sm->assoc_resp_ies,
+				    sm->assoc_resp_ies_len);
 			os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies,
 				  sm->assoc_resp_ies_len);
 			wpa_ie_len += sm->assoc_resp_ies_len;
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 842e5dd..01b2f83 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -130,7 +130,7 @@
 			return -1;
 		}
 
-		der = base64_decode(pos, end - pos, &der_len);
+		der = base64_decode((const char *) pos, end - pos, &der_len);
 		if (der == NULL) {
 			wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
 				   "certificate");
@@ -293,7 +293,7 @@
 		}
 	}
 
-	der = base64_decode(pos, end - pos, &der_len);
+	der = base64_decode((const char *) pos, end - pos, &der_len);
 	if (!der)
 		return NULL;
 	pkey = crypto_private_key_import(der, der_len, NULL);
@@ -321,7 +321,7 @@
 	if (!end)
 		return NULL;
 
-	der = base64_decode(pos, end - pos, &der_len);
+	der = base64_decode((const char *) pos, end - pos, &der_len);
 	if (!der)
 		return NULL;
 	pkey = crypto_private_key_import(der, der_len, passwd);
@@ -1225,7 +1225,7 @@
 		return -1;
 	}
 
-	der = base64_decode(pos, end - pos, &der_len);
+	der = base64_decode((const char *) pos, end - pos, &der_len);
 	if (der == NULL) {
 		wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
 		return -1;
diff --git a/src/utils/base64.c b/src/utils/base64.c
index 53a92f4..a17d2d3 100644
--- a/src/utils/base64.c
+++ b/src/utils/base64.c
@@ -12,18 +12,16 @@
 #include "os.h"
 #include "base64.h"
 
-static const unsigned char base64_table[65] =
+static const char base64_table[65] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static const unsigned char base64_url_table[65] =
+static const char base64_url_table[65] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
 
 
-static unsigned char * base64_gen_encode(const unsigned char *src, size_t len,
-					 size_t *out_len,
-					 const unsigned char *table,
-					 int add_pad)
+static char * base64_gen_encode(const unsigned char *src, size_t len,
+				size_t *out_len, const char *table, int add_pad)
 {
-	unsigned char *out, *pos;
+	char *out, *pos;
 	const unsigned char *end, *in;
 	size_t olen;
 	int line_len;
@@ -83,9 +81,8 @@
 }
 
 
-static unsigned char * base64_gen_decode(const unsigned char *src, size_t len,
-					 size_t *out_len,
-					 const unsigned char *table)
+static unsigned char * base64_gen_decode(const char *src, size_t len,
+					 size_t *out_len, const char *table)
 {
 	unsigned char dtable[256], *out, *pos, block[4], tmp;
 	size_t i, count, olen;
@@ -94,12 +91,12 @@
 
 	os_memset(dtable, 0x80, 256);
 	for (i = 0; i < sizeof(base64_table) - 1; i++)
-		dtable[table[i]] = (unsigned char) i;
+		dtable[(unsigned char) table[i]] = (unsigned char) i;
 	dtable['='] = 0;
 
 	count = 0;
 	for (i = 0; i < len; i++) {
-		if (dtable[src[i]] != 0x80)
+		if (dtable[(unsigned char) src[i]] != 0x80)
 			count++;
 	}
 
@@ -165,17 +162,15 @@
  * nul terminated to make it easier to use as a C string. The nul terminator is
  * not included in out_len.
  */
-unsigned char * base64_encode(const unsigned char *src, size_t len,
-			      size_t *out_len)
+char * base64_encode(const void *src, size_t len, size_t *out_len)
 {
 	return base64_gen_encode(src, len, out_len, base64_table, 1);
 }
 
 
-unsigned char * base64_url_encode(const unsigned char *src, size_t len,
-				  size_t *out_len, int add_pad)
+char * base64_url_encode(const void *src, size_t len, size_t *out_len)
 {
-	return base64_gen_encode(src, len, out_len, base64_url_table, add_pad);
+	return base64_gen_encode(src, len, out_len, base64_url_table, 0);
 }
 
 
@@ -189,15 +184,13 @@
  *
  * Caller is responsible for freeing the returned buffer.
  */
-unsigned char * base64_decode(const unsigned char *src, size_t len,
-			      size_t *out_len)
+unsigned char * base64_decode(const char *src, size_t len, size_t *out_len)
 {
 	return base64_gen_decode(src, len, out_len, base64_table);
 }
 
 
-unsigned char * base64_url_decode(const unsigned char *src, size_t len,
-				  size_t *out_len)
+unsigned char * base64_url_decode(const char *src, size_t len, size_t *out_len)
 {
 	return base64_gen_decode(src, len, out_len, base64_url_table);
 }
diff --git a/src/utils/base64.h b/src/utils/base64.h
index 5a72c3e..6216f44 100644
--- a/src/utils/base64.h
+++ b/src/utils/base64.h
@@ -9,13 +9,9 @@
 #ifndef BASE64_H
 #define BASE64_H
 
-unsigned char * base64_encode(const unsigned char *src, size_t len,
-			      size_t *out_len);
-unsigned char * base64_decode(const unsigned char *src, size_t len,
-			      size_t *out_len);
-unsigned char * base64_url_encode(const unsigned char *src, size_t len,
-				  size_t *out_len, int add_pad);
-unsigned char * base64_url_decode(const unsigned char *src, size_t len,
-				  size_t *out_len);
+char * base64_encode(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);
 
 #endif /* BASE64_H */
diff --git a/src/utils/json.c b/src/utils/json.c
index 3e5e214..5a0edf2 100644
--- a/src/utils/json.c
+++ b/src/utils/json.c
@@ -300,8 +300,10 @@
 				goto fail;
 			if (!curr_token) {
 				token = json_alloc_token(&tokens);
-				if (!token)
+				if (!token) {
+					os_free(str);
 					goto fail;
+				}
 				token->type = JSON_STRING;
 				token->string = str;
 				token->state = JSON_COMPLETED;
@@ -514,8 +516,8 @@
 	token = json_get_member(json, name);
 	if (!token || token->type != JSON_STRING)
 		return NULL;
-	buf = base64_url_decode((const unsigned char *) token->string,
-				os_strlen(token->string), &buflen);
+	buf = base64_url_decode(token->string, os_strlen(token->string),
+				&buflen);
 	if (!buf)
 		return NULL;
 	ret = wpabuf_alloc_ext_data(buf, buflen);
@@ -574,3 +576,79 @@
 	buf[0] = '\0';
 	json_print_token(root, 1, buf, buflen);
 }
+
+
+void json_add_int(struct wpabuf *json, const char *name, int val)
+{
+	wpabuf_printf(json, "\"%s\":%d", name, val);
+}
+
+
+void json_add_string(struct wpabuf *json, const char *name, const char *val)
+{
+	wpabuf_printf(json, "\"%s\":\"%s\"", name, val);
+}
+
+
+int json_add_string_escape(struct wpabuf *json, const char *name,
+			   const void *val, size_t len)
+{
+	char *tmp;
+	size_t tmp_len = 6 * len + 1;
+
+	tmp = os_malloc(tmp_len);
+	if (!tmp)
+		return -1;
+	json_escape_string(tmp, tmp_len, val, len);
+	json_add_string(json, name, tmp);
+	bin_clear_free(tmp, tmp_len);
+	return 0;
+}
+
+
+int json_add_base64url(struct wpabuf *json, const char *name, const void *val,
+		       size_t len)
+{
+	char *b64;
+
+	b64 = base64_url_encode(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)
+		wpabuf_printf(json, "\"%s\":", name);
+	wpabuf_put_u8(json, '{');
+}
+
+
+void json_end_object(struct wpabuf *json)
+{
+	wpabuf_put_u8(json, '}');
+}
+
+
+void json_start_array(struct wpabuf *json, const char *name)
+{
+	if (name)
+		wpabuf_printf(json, "\"%s\":", name);
+	wpabuf_put_u8(json, '[');
+}
+
+
+void json_end_array(struct wpabuf *json)
+{
+	wpabuf_put_u8(json, ']');
+}
+
+
+void json_value_sep(struct wpabuf *json)
+{
+	wpabuf_put_u8(json, ',');
+}
diff --git a/src/utils/json.h b/src/utils/json.h
index 8faa95d..ca4a2e4 100644
--- a/src/utils/json.h
+++ b/src/utils/json.h
@@ -38,5 +38,16 @@
 struct wpabuf * json_get_member_base64url(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);
+int json_add_string_escape(struct wpabuf *json, const char *name,
+			   const void *val, size_t len);
+int json_add_base64url(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);
+void json_end_array(struct wpabuf *json);
+void json_value_sep(struct wpabuf *json);
 
 #endif /* JSON_H */
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index 3af4fcd..b09225d 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -296,52 +296,53 @@
 {
 	int errors = 0;
 	unsigned char *res;
+	char *res2;
 	size_t res_len;
 
 	wpa_printf(MSG_INFO, "base64 tests");
 
-	res = base64_encode((const unsigned char *) "", ~0, &res_len);
+	res2 = base64_encode("", ~0, &res_len);
+	if (res2) {
+		errors++;
+		os_free(res2);
+	}
+
+	res2 = base64_encode("=", 1, &res_len);
+	if (!res2 || res_len != 5 || res2[0] != 'P' || res2[1] != 'Q' ||
+	    res2[2] != '=' || res2[3] != '=' || res2[4] != '\n')
+		errors++;
+	os_free(res2);
+
+	res2 = base64_encode("=", 1, NULL);
+	if (!res2 || res2[0] != 'P' || res2[1] != 'Q' ||
+	    res2[2] != '=' || res2[3] != '=' || res2[4] != '\n')
+		errors++;
+	os_free(res2);
+
+	res = base64_decode("", 0, &res_len);
 	if (res) {
 		errors++;
 		os_free(res);
 	}
 
-	res = base64_encode((const unsigned char *) "=", 1, &res_len);
-	if (!res || res_len != 5 || res[0] != 'P' || res[1] != 'Q' ||
-	    res[2] != '=' || res[3] != '=' || res[4] != '\n')
-		errors++;
-	os_free(res);
-
-	res = base64_encode((const unsigned char *) "=", 1, NULL);
-	if (!res || res[0] != 'P' || res[1] != 'Q' ||
-	    res[2] != '=' || res[3] != '=' || res[4] != '\n')
-		errors++;
-	os_free(res);
-
-	res = base64_decode((const unsigned char *) "", 0, &res_len);
+	res = base64_decode("a", 1, &res_len);
 	if (res) {
 		errors++;
 		os_free(res);
 	}
 
-	res = base64_decode((const unsigned char *) "a", 1, &res_len);
+	res = base64_decode("====", 4, &res_len);
 	if (res) {
 		errors++;
 		os_free(res);
 	}
 
-	res = base64_decode((const unsigned char *) "====", 4, &res_len);
-	if (res) {
-		errors++;
-		os_free(res);
-	}
-
-	res = base64_decode((const unsigned char *) "PQ==", 4, &res_len);
+	res = base64_decode("PQ==", 4, &res_len);
 	if (!res || res_len != 1 || res[0] != '=')
 		errors++;
 	os_free(res);
 
-	res = base64_decode((const unsigned char *) "P.Q-=!=*", 8, &res_len);
+	res = base64_decode("P.Q-=!=*", 8, &res_len);
 	if (!res || res_len != 1 || res[0] != '=')
 		errors++;
 	os_free(res);
diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c
index 7b6d276..d73654e 100644
--- a/src/utils/xml_libxml2.c
+++ b/src/utils/xml_libxml2.c
@@ -409,7 +409,7 @@
 	if (txt == NULL)
 		return NULL;
 
-	ret = base64_decode((unsigned char *) txt, strlen(txt), &len);
+	ret = base64_decode(txt, strlen(txt), &len);
 	if (ret_len)
 		*ret_len = len;
 	xml_node_get_text_free(ctx, txt);
diff --git a/src/wps/upnp_xml.c b/src/wps/upnp_xml.c
index a9958ee..ca0925c 100644
--- a/src/wps/upnp_xml.c
+++ b/src/wps/upnp_xml.c
@@ -235,7 +235,7 @@
 		return NULL;
 	}
 
-	decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
+	decoded = base64_decode(msg, os_strlen(msg), &len);
 	os_free(msg);
 	if (decoded == NULL) {
 		*ret = UPNP_OUT_OF_MEMORY;
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 06a8fda..6bded14 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -897,7 +897,7 @@
 				       const struct sockaddr_in *dst,
 				       char **len_ptr, char **body_ptr)
 {
-	unsigned char *encoded;
+	char *encoded;
 	size_t encoded_len;
 	struct wpabuf *buf;
 
@@ -939,7 +939,7 @@
 	wpabuf_put_str(buf, "\">\n");
 	if (encoded) {
 		wpabuf_printf(buf, "<%s>%s</%s>\n",
-			      arg_name, (char *) encoded, arg_name);
+			      arg_name, encoded, arg_name);
 		os_free(encoded);
 	}
 
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 0ac5b28..671f5fe 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1745,7 +1745,8 @@
 			return -1;
 		}
 		os_free(wps->new_psk);
-		wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
+		wps->new_psk = (u8 *) base64_encode(r, sizeof(r),
+						    &wps->new_psk_len);
 		if (wps->new_psk == NULL)
 			return -1;
 		wps->new_psk_len--; /* remove newline */
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index ca893a4..6e10e4b 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -519,8 +519,9 @@
 
 	dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
 			      list) {
-		event_add(s, buf,
-			  sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
+		wps_upnp_event_add(
+			s, buf,
+			sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
 	}
 
 	wpabuf_free(buf);
@@ -541,7 +542,7 @@
 	struct upnp_wps_device_interface *iface;
 	wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
 	subscr_addr_free_all(s);
-	event_delete_all(s);
+	wps_upnp_event_delete_all(s);
 	dl_list_for_each(iface, &s->sm->interfaces,
 			 struct upnp_wps_device_interface, list)
 		upnp_er_remove_notification(iface->wps->registrar, s);
@@ -647,7 +648,7 @@
 			   "initial WLANEvent");
 		msg = build_fake_wsc_ack();
 		if (msg) {
-			s->sm->wlanevent = (char *)
+			s->sm->wlanevent =
 				base64_encode(wpabuf_head(msg),
 					      wpabuf_len(msg), NULL);
 			wpabuf_free(msg);
@@ -672,7 +673,7 @@
 		wpabuf_put_property(buf, "WLANEvent", wlan_event);
 	wpabuf_put_str(buf, tail);
 
-	ret = event_add(s, buf, 0);
+	ret = wps_upnp_event_add(s, buf, 0);
 	if (ret) {
 		wpabuf_free(buf);
 		return ret;
@@ -749,7 +750,7 @@
 		   "WPS UPnP: Subscription %p (SID %s) started with %s",
 		   s, str, callback_urls);
 	/* Schedule sending this */
-	event_send_all_later(sm);
+	wps_upnp_event_send_all_later(sm);
 	return s;
 }
 
@@ -822,7 +823,7 @@
 	}
 	raw_len = pos;
 
-	val = (char *) base64_encode(raw, raw_len, &val_len);
+	val = base64_encode(raw, raw_len, &val_len);
 	if (val == NULL)
 		goto fail;
 
@@ -987,7 +988,7 @@
 
 	advertisement_state_machine_stop(sm, 1);
 
-	event_send_stop_all(sm);
+	wps_upnp_event_send_stop_all(sm);
 	os_free(sm->wlanevent);
 	sm->wlanevent = NULL;
 	os_free(sm->ip_addr_text);
diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c
index 94aae75..d7e6edc 100644
--- a/src/wps/wps_upnp_event.c
+++ b/src/wps/wps_upnp_event.c
@@ -96,8 +96,8 @@
 }
 
 
-/* event_delete_all -- delete entire event queue and current event */
-void event_delete_all(struct subscription *s)
+/* wps_upnp_event_delete_all -- delete entire event queue and current event */
+void wps_upnp_event_delete_all(struct subscription *s)
 {
 	struct wps_event_ *e;
 	while ((e = event_dequeue(s)) != NULL)
@@ -134,11 +134,11 @@
 		event_delete(e);
 		s->last_event_failed = 1;
 		if (!dl_list_empty(&s->event_queue))
-			event_send_all_later(s->sm);
+			wps_upnp_event_send_all_later(s->sm);
 		return;
 	}
 	dl_list_add(&s->event_queue, &e->list);
-	event_send_all_later(sm);
+	wps_upnp_event_send_all_later(sm);
 }
 
 
@@ -228,7 +228,7 @@
 
 		/* Schedule sending more if there is more to send */
 		if (!dl_list_empty(&s->event_queue))
-			event_send_all_later(s->sm);
+			wps_upnp_event_send_all_later(s->sm);
 		break;
 	case HTTP_CLIENT_FAILED:
 		wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
@@ -328,19 +328,19 @@
 
 	if (nerrors) {
 		/* Try again later */
-		event_send_all_later(sm);
+		wps_upnp_event_send_all_later(sm);
 	}
 }
 
 
-/* event_send_all_later -- schedule sending events to all subscribers
+/* wps_upnp_event_send_all_later -- schedule sending events to all subscribers
  * that need it.
  * This avoids two problems:
  * -- After getting a subscription, we should not send the first event
  *      until after our reply is fully queued to be sent back,
  * -- Possible stack depth or infinite recursion issues.
  */
-void event_send_all_later(struct upnp_wps_device_sm *sm)
+void wps_upnp_event_send_all_later(struct upnp_wps_device_sm *sm)
 {
 	/*
 	 * The exact time in the future isn't too important. Waiting a bit
@@ -354,8 +354,8 @@
 }
 
 
-/* event_send_stop_all -- cleanup */
-void event_send_stop_all(struct upnp_wps_device_sm *sm)
+/* wps_upnp_event_send_stop_all -- cleanup */
+void wps_upnp_event_send_stop_all(struct upnp_wps_device_sm *sm)
 {
 	if (sm->event_send_all_queued)
 		eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
@@ -364,13 +364,14 @@
 
 
 /**
- * event_add - Add a new event to a queue
+ * wps_upnp_event_add - Add a new event to a queue
  * @s: Subscription
  * @data: Event data (is copied; caller retains ownership)
  * @probereq: Whether this is a Probe Request event
  * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
  */
-int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
+int wps_upnp_event_add(struct subscription *s, const struct wpabuf *data,
+		       int probereq)
 {
 	struct wps_event_ *e;
 	unsigned int len;
@@ -416,6 +417,6 @@
 	wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
 		   "(queue len %u)", e, s, len + 1);
 	dl_list_add_tail(&s->event_queue, &e->list);
-	event_send_all_later(s->sm);
+	wps_upnp_event_send_all_later(s->sm);
 	return 0;
 }
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
index 6a7c627..e87a932 100644
--- a/src/wps/wps_upnp_i.h
+++ b/src/wps/wps_upnp_i.h
@@ -177,10 +177,11 @@
 void web_listener_stop(struct upnp_wps_device_sm *sm);
 
 /* wps_upnp_event.c */
-int event_add(struct subscription *s, const struct wpabuf *data, int probereq);
-void event_delete_all(struct subscription *s);
-void event_send_all_later(struct upnp_wps_device_sm *sm);
-void event_send_stop_all(struct upnp_wps_device_sm *sm);
+int wps_upnp_event_add(struct subscription *s, const struct wpabuf *data,
+		       int probereq);
+void wps_upnp_event_delete_all(struct subscription *s);
+void wps_upnp_event_send_all_later(struct upnp_wps_device_sm *sm);
+void wps_upnp_event_send_stop_all(struct upnp_wps_device_sm *sm);
 
 /* wps_upnp_ap.c */
 int upnp_er_set_selected_registrar(struct wps_registrar *reg,
diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c
index 7548e84..3c5a97c 100644
--- a/src/wps/wps_upnp_web.c
+++ b/src/wps/wps_upnp_web.c
@@ -765,8 +765,8 @@
 
 	if (reply) {
 		size_t len;
-		replydata = (char *) base64_encode(wpabuf_head(reply),
-						   wpabuf_len(reply), &len);
+		replydata = base64_encode(wpabuf_head(reply), wpabuf_len(reply),
+					  &len);
 	} else
 		replydata = NULL;